static void
cert_writer (NMConnection *connection,
             GKeyFile *file,
             NMKeyfileWriteTypeDataCert *cert_data,
             WriteInfo *info,
             GError **error)
{
	const char *setting_name = nm_setting_get_name (NM_SETTING (cert_data->setting));
	NMSetting8021xCKScheme scheme;
	NMSetting8021xCKFormat format;
	const char *path = NULL, *ext = "pem";

	scheme = cert_data->scheme_func (cert_data->setting);
	if (scheme == NM_SETTING_802_1X_CK_SCHEME_PATH) {
		char *tmp = NULL;
		const char *accepted_path = NULL;

		path = cert_data->path_func (cert_data->setting);
		g_assert (path);

		if (g_str_has_prefix (path, info->keyfile_dir)) {
			const char *p = path + strlen (info->keyfile_dir);

			/* If the path is rooted in the keyfile directory, just use a
			 * relative path instead of an absolute one.
			 */
			if (*p == '/') {
				while (*p == '/')
					p++;
				if (p[0]) {
					/* If @p looks like an integer list, the following detection will fail too and
					 * we will file:// qualify the path below. We thus avoid writing a path string
					 * that would be interpreted as legacy binary format by reader. */
					tmp = nm_keyfile_detect_unqualified_path_scheme (info->keyfile_dir, p, -1, FALSE, NULL);
					if (tmp) {
						g_clear_pointer (&tmp, g_free);
						accepted_path = p;
					}
				}
			}
		}
		if (!accepted_path) {
			/* What we are about to write, must also be understood by the reader.
			 * Otherwise, add a file:// prefix */
			tmp = nm_keyfile_detect_unqualified_path_scheme (info->keyfile_dir, path, -1, FALSE, NULL);
			if (tmp) {
				g_clear_pointer (&tmp, g_free);
				accepted_path = path;
			}
		}

		if (!accepted_path)
			accepted_path = tmp = g_strconcat (NM_KEYFILE_CERT_SCHEME_PREFIX_PATH, path, NULL);
		nm_keyfile_plugin_kf_set_string (file, setting_name, cert_data->property_name, accepted_path);
		g_free (tmp);
	} else if (scheme == NM_SETTING_802_1X_CK_SCHEME_BLOB) {
		GBytes *blob;
		const guint8 *blob_data;
		gsize blob_len;
		gboolean success;
		GError *local = NULL;
		char *new_path;

		blob = cert_data->blob_func (cert_data->setting);
		g_assert (blob);
		blob_data = g_bytes_get_data (blob, &blob_len);

		if (cert_data->format_func) {
			/* Get the extension for a private key */
			format = cert_data->format_func (cert_data->setting);
			if (format == NM_SETTING_802_1X_CK_FORMAT_PKCS12)
				ext = "p12";
		} else {
			/* DER or PEM format certificate? */
			if (blob_len > 2 && blob_data[0] == 0x30 && blob_data[1] == 0x82)
				ext = "der";
		}

		/* Write the raw data out to the standard file so that we can use paths
		 * from now on instead of pushing around the certificate data.
		 */
		new_path = g_strdup_printf ("%s/%s-%s.%s", info->keyfile_dir, nm_connection_get_uuid (connection),
		                            cert_data->suffix, ext);

		success = write_cert_key_file (new_path, blob_data, blob_len, &local);
		if (success) {
			/* Write the path value to the keyfile.
			 * We know, that basename(new_path) starts with a UUID, hence no conflict with "data:;base64,"  */
			nm_keyfile_plugin_kf_set_string (file, setting_name, cert_data->property_name, strrchr (new_path, '/') + 1);
		} else {
			nm_log_warn (LOGD_SETTINGS, "keyfile: %s.%s: failed to write certificate to file %s: %s",
			             setting_name, cert_data->property_name, new_path, local->message);
			g_error_free (local);
		}
		g_free (new_path);
	} else {
		/* scheme_func() returns UNKNOWN in all other cases. The only valid case
		 * where a scheme is allowed to be UNKNOWN, is unsetting the value. In this
		 * case, we don't expect the writer to be called, because the default value
		 * will not be serialized.
		 * The only other reason for the scheme to be UNKNOWN is an invalid cert.
		 * But our connection verifies, so that cannot happen either. */
		g_return_if_reached ();
	}
}
Пример #2
0
static void
cert_writer (GKeyFile *file,
             const char *keyfile_dir,
             const char *uuid,
             NMSetting *setting,
             const char *key,
             const GValue *value)
{
	const char *setting_name = nm_setting_get_name (setting);
	NMSetting8021xCKScheme scheme;
	NMSetting8021xCKFormat format;
	const char *path = NULL, *ext = "pem";
	const ObjectType *objtype = NULL;
	int i;

	for (i = 0; i < G_N_ELEMENTS (objtypes) && objtypes[i].key; i++) {
		if (g_strcmp0 (objtypes[i].key, key) == 0) {
			objtype = &objtypes[i];
			break;
		}
	}
	g_return_if_fail (objtype != NULL);

	scheme = objtypes->scheme_func (NM_SETTING_802_1X (setting));
	if (scheme == NM_SETTING_802_1X_CK_SCHEME_PATH) {
		path = objtype->path_func (NM_SETTING_802_1X (setting));
		g_assert (path);

		/* If the path is rooted in the keyfile directory, just use a
		 * relative path instead of an absolute one.
		 */
		if (g_str_has_prefix (path, keyfile_dir)) {
			path += strlen (keyfile_dir);
			while (*path == '/')
				path++;
		}

		g_key_file_set_string (file, setting_name, key, path);
	} else if (scheme == NM_SETTING_802_1X_CK_SCHEME_BLOB) {
		const GByteArray *blob;
		gboolean success;
		GError *error = NULL;
		char *new_path;

		blob = objtype->blob_func (NM_SETTING_802_1X (setting));
		g_assert (blob);

		if (objtype->format_func) {
			/* Get the extension for a private key */
			format = objtype->format_func (NM_SETTING_802_1X (setting));
			if (format == NM_SETTING_802_1X_CK_FORMAT_PKCS12)
				ext = "p12";
		} else {
			/* DER or PEM format certificate? */
			if (blob->len > 2 && blob->data[0] == 0x30 && blob->data[1] == 0x82)
				ext = "der";
		}

		/* Write the raw data out to the standard file so that we can use paths
		 * from now on instead of pushing around the certificate data.
		 */
		new_path = g_strdup_printf ("%s/%s-%s.%s", keyfile_dir, uuid, objtype->suffix, ext);
		g_assert (new_path);

		success = write_cert_key_file (new_path, blob, &error);
		if (success) {
			/* Write the path value to the keyfile */
			g_key_file_set_string (file, setting_name, key, new_path);
		} else {
			g_warning ("Failed to write certificate/key %s: %s", new_path, error->message);
			g_error_free (error);
		}
		g_free (new_path);
	} else
		g_assert_not_reached ();
}