From 5d14deff4a93e664ff561dd04333dee10a1ce1e5 Mon Sep 17 00:00:00 2001 From: Richard Hughes Date: Wed, 7 Oct 2015 17:43:10 +0100 Subject: [PATCH] Support cabinet archives files with more than one firmware This allows a vendor to upload a single file that targets different versions of the same hardware. If this feature is used, the metainfo.xml files *must* have something like inside the latest tag. --- configure.ac | 2 +- src/Makefile.am | 4 - src/fu-cab.c | 763 ----------------------------------------- src/fu-cab.h | 88 ----- src/fu-keyring.c | 159 --------- src/fu-keyring.h | 6 - src/fu-main.c | 379 ++++++++++++++------ src/fu-provider-chug.c | 14 +- src/fu-provider-fake.c | 2 +- src/fu-provider-rpi.c | 6 +- src/fu-provider-uefi.c | 30 +- src/fu-provider.c | 26 +- src/fu-provider.h | 8 +- src/fu-self-test.c | 60 ---- 14 files changed, 329 insertions(+), 1218 deletions(-) delete mode 100644 src/fu-cab.c delete mode 100644 src/fu-cab.h diff --git a/configure.ac b/configure.ac index ceebad784..f94b41ded 100644 --- a/configure.ac +++ b/configure.ac @@ -119,7 +119,7 @@ PKG_CHECK_MODULES(GLIB, glib-2.0 >= 2.45.8 gobject-2.0 gthread-2.0 gio-2.0 >= 2. PKG_CHECK_MODULES(GUDEV, gudev-1.0) PKG_CHECK_MODULES(POLKIT, polkit-gobject-1 >= 0.103) PKG_CHECK_MODULES(GCAB, libgcab-1.0) -PKG_CHECK_MODULES(APPSTREAM_GLIB, appstream-glib >= 0.5.1) +PKG_CHECK_MODULES(APPSTREAM_GLIB, appstream-glib >= 0.5.2) PKG_CHECK_MODULES(GUSB, gusb >= 0.2.2) PKG_CHECK_MODULES(SQLITE, sqlite3) PKG_CHECK_MODULES(ARCHIVE, libarchive) diff --git a/src/Makefile.am b/src/Makefile.am index 922365431..eef22ef84 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -83,8 +83,6 @@ pkglibexec_PROGRAMS = \ fwupd_SOURCES = \ fu-cleanup.h \ - fu-cab.c \ - fu-cab.h \ fu-debug.c \ fu-debug.h \ fu-device.c \ @@ -151,8 +149,6 @@ check_PROGRAMS = \ fu-self-test fu_self_test_SOURCES = \ - fu-cab.c \ - fu-cab.h \ fu-device.c \ fu-device.h \ fu-keyring.c \ diff --git a/src/fu-cab.c b/src/fu-cab.c deleted file mode 100644 index 962ca1d2d..000000000 --- a/src/fu-cab.c +++ /dev/null @@ -1,763 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- - * - * Copyright (C) 2015 Richard Hughes - * - * Licensed under the GNU General Public License Version 2 - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#include "config.h" - -#include -#include -#include -#include -#include -#include -#include - -#include "fu-cleanup.h" -#include "fu-cab.h" -#include "fu-keyring.h" - -static void fu_cab_finalize (GObject *object); - -/** - * FuCabPrivate: - * - * Private #FuCab data - **/ -typedef struct { - GCabCabinet *gcab; - GInputStream *cab_stream; - GKeyFile *inf_kf; - FwupdTrustFlags trust_flags; - gchar *firmware_basename; - gchar *firmware_filename; - gchar *signature_basename; - gchar *cat_basename; - gchar *description; - gchar *guid; - gchar *inf_basename; - gchar *metainfo_basename; - gchar *tmp_path; - gchar *license; - gchar *name; - gchar *summary; - gchar *url_homepage; - gchar *vendor; - gchar *version; - guint64 size; - GPtrArray *basenames_to_delete; - GPtrArray *filelist; /* with full path */ -} FuCabPrivate; - -G_DEFINE_TYPE_WITH_PRIVATE (FuCab, fu_cab, G_TYPE_OBJECT) -#define GET_PRIVATE(o) (fu_cab_get_instance_private (o)) - -typedef struct { - FuCab *cab; - FuCabExtractFlags flags; -} FuCabExtractHelper; - -/** - * fu_cab_add_file: - **/ -void -fu_cab_add_file (FuCab *cab, const gchar *filename) -{ - FuCabPrivate *priv = GET_PRIVATE (cab); - const gchar *tmp; - guint i; - g_autofree gchar *basename = NULL; - - /* check the same basename does not already exist */ - basename = g_path_get_basename (filename); - for (i = 0; i < priv->filelist->len; i++) { - g_autofree gchar *basename_tmp = NULL; - tmp = g_ptr_array_index (priv->filelist, i); - basename_tmp = g_path_get_basename (tmp); - if (g_strcmp0 (basename_tmp, basename) == 0) { - g_debug ("%s already exists, removing old", basename_tmp); - g_ptr_array_remove_index (priv->filelist, i); - break; - } - } - - /* add the full filename */ - g_ptr_array_add (priv->filelist, g_strdup (filename)); -} - -/** - * fu_cab_read_file_list_cb: - **/ -static gboolean -fu_cab_read_file_list_cb (GCabFile *file, gpointer user_data) -{ - FuCab *cab = FU_CAB (user_data); - FuCabPrivate *priv = GET_PRIVATE (cab); - g_ptr_array_add (priv->filelist, - g_build_filename (priv->tmp_path, - gcab_file_get_name (file), - NULL)); - return FALSE; -} - -/** - * fu_cab_match_basename_flag: - **/ -static FuCabExtractFlags -fu_cab_match_basename_flag (FuCab *cab, const gchar *basename) -{ - FuCabPrivate *priv = GET_PRIVATE (cab); - if (g_strcmp0 (basename, priv->firmware_basename) == 0) - return FU_CAB_EXTRACT_FLAG_FIRMWARE; - if (g_strcmp0 (basename, priv->signature_basename) == 0) - return FU_CAB_EXTRACT_FLAG_SIGNATURE; - if (g_strcmp0 (basename, priv->inf_basename) == 0) - return FU_CAB_EXTRACT_FLAG_INF; - if (g_strcmp0 (basename, priv->metainfo_basename) == 0) - return FU_CAB_EXTRACT_FLAG_METAINFO; - if (g_strcmp0 (basename, priv->cat_basename) == 0) - return FU_CAB_EXTRACT_FLAG_CATALOG; - return FU_CAB_EXTRACT_FLAG_UNKNOWN; -} - -/** - * fu_cab_extract_cb: - **/ -static gboolean -fu_cab_extract_cb (GCabFile *file, gpointer user_data) -{ - FuCabExtractHelper *helper = (FuCabExtractHelper *) user_data; - FuCab *cab = FU_CAB (helper->cab); - FuCabPrivate *priv = GET_PRIVATE (cab); - FuCabExtractFlags match_flags; - - /* only if it matches the mask */ - match_flags = fu_cab_match_basename_flag (cab, gcab_file_get_name (file)); - if ((helper->flags & match_flags) > 0) { - g_ptr_array_add (priv->basenames_to_delete, - g_strdup (gcab_file_get_name (file))); - return TRUE; - } - - return helper->flags == FU_CAB_EXTRACT_FLAG_ALL; -} - -/** - * fu_cab_parse: - **/ -static gboolean -fu_cab_parse (FuCab *cab, GError **error) -{ - AsRelease *rel; - FuCabPrivate *priv = GET_PRIVATE (cab); - GString *update_description; - const gchar *tmp; - guint i; - const gchar *fn; - g_autoptr(GError) error_local = NULL; - g_autofree gchar *inf_filename = NULL; - g_autoptr(AsApp) app = NULL; - g_autoptr(GFile) path = NULL; - - g_return_val_if_fail (FU_IS_CAB (cab), FALSE); - - /* open the file */ - priv->gcab = gcab_cabinet_new (); - if (!gcab_cabinet_load (priv->gcab, priv->cab_stream, NULL, &error_local)) { - g_set_error (error, - FWUPD_ERROR, - FWUPD_ERROR_READ, - "cannot load .cab file: %s", - error_local->message); - return FALSE; - } - - /* decompress to /tmp */ - priv->tmp_path = g_dir_make_tmp ("fwupd-XXXXXX", &error_local); - if (priv->tmp_path == NULL) { - g_set_error (error, - FWUPD_ERROR, - FWUPD_ERROR_WRITE, - "failed to create temp dir: %s", - error_local->message); - return FALSE; - } - - /* get the file list */ - path = g_file_new_for_path (priv->tmp_path); - if (!gcab_cabinet_extract_simple (priv->gcab, path, - fu_cab_read_file_list_cb, - cab, NULL, &error_local)) { - g_set_error (error, - FWUPD_ERROR, - FWUPD_ERROR_WRITE, - "failed to extract .cab file: %s", - error_local->message); - return FALSE; - } - - /* find the .inf file in the file list */ - for (i = 0; i < priv->filelist->len; i++) { - fn = g_ptr_array_index (priv->filelist, i); - if (g_str_has_suffix (fn, ".inf")) { - priv->inf_basename = g_path_get_basename (fn); - continue; - } - if (g_str_has_suffix (fn, ".metainfo.xml")) { - priv->metainfo_basename = g_path_get_basename (fn); - continue; - } - } - - /* read .inf file */ - if (priv->inf_basename == NULL) { - g_set_error (error, - FWUPD_ERROR, - FWUPD_ERROR_INVALID_FILE, - "no .inf file in.cab file"); - return FALSE; - } - - /* extract these */ - if (!fu_cab_extract (cab, FU_CAB_EXTRACT_FLAG_INF | - FU_CAB_EXTRACT_FLAG_METAINFO, error)) - return FALSE; - - /* parse it */ - app = as_app_new (); - inf_filename = g_build_filename (priv->tmp_path, priv->inf_basename, NULL); - if (!as_app_parse_file (app, inf_filename, - AS_APP_PARSE_FLAG_NONE, &error_local)) { - g_set_error (error, - FWUPD_ERROR, - FWUPD_ERROR_INVALID_FILE, - "%s could not be loaded: %s", - inf_filename, error_local->message); - return FALSE; - } - - /* merge with the metainfo file */ - if (priv->metainfo_basename != NULL) { - g_autofree gchar *metainfo_filename = NULL; - g_autoptr(AsApp) app2 = NULL; - app2 = as_app_new (); - metainfo_filename = g_build_filename (priv->tmp_path, - priv->metainfo_basename, NULL); - if (!as_app_parse_file (app2, metainfo_filename, - AS_APP_PARSE_FLAG_NONE, &error_local)) { - g_set_error (error, - FWUPD_ERROR, - FWUPD_ERROR_INVALID_FILE, - "%s could not be loaded: %s", - metainfo_filename, - error_local->message); - return FALSE; - } - as_app_subsume_full (app, app2, AS_APP_SUBSUME_FLAG_NONE); - } - - /* extract info */ - update_description = g_string_new (""); - priv->guid = g_strdup (as_app_get_id (app)); - priv->vendor = g_strdup (as_app_get_developer_name (app, NULL)); - priv->name = g_strdup (as_app_get_name (app, NULL)); - priv->summary = g_strdup (as_app_get_comment (app, NULL)); - tmp = as_app_get_description (app, NULL); - if (tmp != NULL) - g_string_append (update_description, tmp); - priv->url_homepage = g_strdup (as_app_get_url_item (app, AS_URL_KIND_HOMEPAGE)); - priv->license = g_strdup (as_app_get_project_license (app)); - rel = as_app_get_release_default (app); - priv->version = g_strdup (as_release_get_version (rel)); - tmp = as_release_get_description (rel, NULL); - if (tmp != NULL) - g_string_append (update_description, tmp); - priv->description = g_string_free (update_description, FALSE); - - /* optional */ - tmp = as_app_get_metadata_item (app, "CatalogBasename"); - if (tmp != NULL) - priv->cat_basename = g_strdup (tmp); - - /* find out what firmware file we have to open */ - tmp = as_app_get_metadata_item (app, "FirmwareBasename"); - priv->firmware_basename = g_strdup (tmp); - priv->firmware_filename = g_build_filename (priv->tmp_path, tmp, NULL); - priv->signature_basename = g_strdup_printf ("%s.asc", tmp); - - /* success */ - return TRUE; -} - -/** - * fu_cab_load_fd: - **/ -gboolean -fu_cab_load_fd (FuCab *cab, gint fd, GCancellable *cancellable, GError **error) -{ - FuCabPrivate *priv = GET_PRIVATE (cab); - g_autoptr(GError) error_local = NULL; - g_autoptr(GInputStream) stream = NULL; - - g_return_val_if_fail (FU_IS_CAB (cab), FALSE); - - /* we can't get the size of the files in the .cab file, so just return - * the size of the cab file itself, on the logic that the firmware will - * be the largest thing by far, and typically be uncompressable */ - priv->size = 0; - - /* GCab needs a GSeekable input stream, so buffer to RAM then load */ - stream = g_unix_input_stream_new (fd, TRUE); - priv->cab_stream = g_memory_input_stream_new (); - while (1) { - g_autoptr(GBytes) data = NULL; - data = g_input_stream_read_bytes (stream, 8192, - cancellable, - &error_local); - if (g_bytes_get_size (data) == 0) - break; - if (data == NULL) { - g_set_error_literal (error, - FWUPD_ERROR, - FWUPD_ERROR_READ, - error_local->message); - return FALSE; - } - priv->size += g_bytes_get_size (data); - g_memory_input_stream_add_bytes (G_MEMORY_INPUT_STREAM (priv->cab_stream), data); - } - - /* parse */ - return fu_cab_parse (cab, error); -} - -/** - * fu_cab_save_file: - **/ -gboolean -fu_cab_save_file (FuCab *cab, GFile *file, GCancellable *cancellable, GError **error) -{ - FuCabPrivate *priv = GET_PRIVATE (cab); - const gchar *tmp; - guint i; - _cleanup_object_unref_ GCabCabinet *gcab = NULL; - _cleanup_object_unref_ GCabFolder *folder = NULL; - g_autoptr(GOutputStream) stream = NULL; - - g_return_val_if_fail (FU_IS_CAB (cab), FALSE); - - /* ensure all files are decompressed */ - if (!fu_cab_extract (cab, FU_CAB_EXTRACT_FLAG_ALL, error)) - return FALSE; - - /* create a new archive, we can't reuse the existing instance */ - gcab = gcab_cabinet_new (); - folder = gcab_folder_new (GCAB_COMPRESSION_NONE); - for (i = 0; i < priv->filelist->len; i++) { - g_autofree gchar *name = NULL; - _cleanup_object_unref_ GCabFile *gfile = NULL; - g_autoptr(GFile) file_tmp = NULL; - - /* only write basename as name */ - tmp = g_ptr_array_index (priv->filelist, i); - name = g_path_get_basename (tmp); - - /* add each file in turn */ - file_tmp = g_file_new_for_path (tmp); - gfile = gcab_file_new_with_file (name, file_tmp); - if (!gcab_folder_add_file (folder, gfile, FALSE, - cancellable, error)) - return FALSE; - } - if (!gcab_cabinet_add_folder (gcab, folder, error)) - return FALSE; - - /* write in one chunk */ - stream = G_OUTPUT_STREAM (g_file_replace (file, NULL, FALSE, - 0, NULL, error)); - if (stream == NULL) - return FALSE; - if (!gcab_cabinet_write_simple (gcab, stream, - NULL, NULL, - cancellable, error)) - return FALSE; - - return TRUE; -} - -/** - * fu_cab_load_file: - **/ -gboolean -fu_cab_load_file (FuCab *cab, GFile *file, GCancellable *cancellable, GError **error) -{ - FuCabPrivate *priv = GET_PRIVATE (cab); - g_autoptr(GError) error_local = NULL; - g_autoptr(GFileInfo) info = NULL; - - g_return_val_if_fail (FU_IS_CAB (cab), FALSE); - - /* get size */ - info = g_file_query_info (file, G_FILE_ATTRIBUTE_STANDARD_SIZE, - G_FILE_QUERY_INFO_NONE, cancellable, &error_local); - if (info == NULL) { - g_autofree gchar *filename = NULL; - filename = g_file_get_path (file); - g_set_error (error, - FWUPD_ERROR, - FWUPD_ERROR_INVALID_FILE, - "Failed to get info for %s: %s", - filename, error_local->message); - return FALSE; - } - priv->size = g_file_info_get_attribute_uint64 (info, G_FILE_ATTRIBUTE_STANDARD_SIZE); - - /* open file */ - priv->cab_stream = G_INPUT_STREAM (g_file_read (file, cancellable, &error_local)); - if (priv->cab_stream == NULL) { - g_autofree gchar *filename = NULL; - filename = g_file_get_path (file); - g_set_error (error, - FWUPD_ERROR, - FWUPD_ERROR_INVALID_FILE, - "Failed to open %s: %s", - filename, error_local->message); - return FALSE; - } - - /* parse */ - return fu_cab_parse (cab, error); -} - -/** - * fu_cab_extract: - **/ -gboolean -fu_cab_extract (FuCab *cab, FuCabExtractFlags flags, GError **error) -{ - FuCabPrivate *priv = GET_PRIVATE (cab); - FuCabExtractHelper helper; - g_autoptr(GError) error_local = NULL; - g_autoptr(GFile) path = NULL; - - g_return_val_if_fail (FU_IS_CAB (cab), FALSE); - - /* extract anything we need */ - helper.cab = cab; - helper.flags = flags; - path = g_file_new_for_path (priv->tmp_path); - if (!gcab_cabinet_extract_simple (priv->gcab, path, - fu_cab_extract_cb, &helper, - NULL, &error_local)) { - g_set_error (error, - FWUPD_ERROR, - FWUPD_ERROR_WRITE, - "failed to extract .cab file: %s", - error_local->message); - return FALSE; - } - - return TRUE; -} - -/** - * fu_cab_verify: - **/ -gboolean -fu_cab_verify (FuCab *cab, GError **error) -{ - FuCabPrivate *priv = GET_PRIVATE (cab); - g_autoptr(GError) error_local = NULL; - g_autofree gchar *pki_dir = NULL; - g_autofree gchar *signature = NULL; - g_autofree gchar *fn = NULL; - g_autoptr(FuKeyring) kr = NULL; - - g_return_val_if_fail (FU_IS_CAB (cab), FALSE); - - /* no valid firmware extracted */ - if (priv->firmware_basename == NULL) { - g_set_error (error, - FWUPD_ERROR, - FWUPD_ERROR_INVALID_FILE, - "%s not already extracted", - priv->firmware_basename); - return FALSE; - } - - /* check we were installed correctly */ - pki_dir = g_build_filename (SYSCONFDIR, "pki", "fwupd", NULL); - if (!g_file_test (pki_dir, G_FILE_TEST_EXISTS)) { - g_set_error (error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_FOUND, - "PKI directory %s not found", pki_dir); - return FALSE; - } - - /* load signature */ - fn = g_build_filename (priv->tmp_path, priv->signature_basename, NULL); - if (!g_file_test (fn, G_FILE_TEST_EXISTS)) { - g_debug ("firmware archive contained no GPG signature"); - return TRUE; - } - if (!g_file_get_contents (fn, &signature, NULL, error)) - return FALSE; - - /* verify against the system trusted keys */ - kr = fu_keyring_new (); - if (!fu_keyring_add_public_keys (kr, pki_dir, error)) - return FALSE; - if (!fu_keyring_verify_file (kr, priv->firmware_filename, - signature, &error_local)) { - g_warning ("untrusted as failed to verify: %s", - error_local->message); - } else { - g_debug ("marking payload as trusted"); - priv->trust_flags |= FWUPD_TRUST_FLAG_PAYLOAD; - } - - return TRUE; -} - -/** - * fu_cab_delete_temp_files: - **/ -gboolean -fu_cab_delete_temp_files (FuCab *cab, GError **error) -{ - FuCabPrivate *priv = GET_PRIVATE (cab); - g_return_val_if_fail (FU_IS_CAB (cab), FALSE); - - if (priv->tmp_path != NULL) { - const gchar *tmp; - guint i; - for (i = 0; i < priv->basenames_to_delete->len; i++) { - g_autofree gchar *fn = NULL; - tmp = g_ptr_array_index (priv->basenames_to_delete, i); - fn = g_build_filename (priv->tmp_path, tmp, NULL); - g_unlink (fn); - } - g_rmdir (priv->tmp_path); - g_ptr_array_set_size (priv->basenames_to_delete, 0); - } - - return TRUE; -} - -/** - * fu_cab_get_stream: - **/ -GInputStream * -fu_cab_get_stream (FuCab *cab) -{ - FuCabPrivate *priv = GET_PRIVATE (cab); - g_return_val_if_fail (FU_IS_CAB (cab), NULL); - return priv->cab_stream; -} - -/** - * fu_cab_get_guid: - **/ -const gchar * -fu_cab_get_guid (FuCab *cab) -{ - FuCabPrivate *priv = GET_PRIVATE (cab); - g_return_val_if_fail (FU_IS_CAB (cab), NULL); - return priv->guid; -} - -/** - * fu_cab_get_version: - **/ -const gchar * -fu_cab_get_version (FuCab *cab) -{ - FuCabPrivate *priv = GET_PRIVATE (cab); - g_return_val_if_fail (FU_IS_CAB (cab), NULL); - return priv->version; -} - -/** - * fu_cab_get_vendor: - **/ -const gchar * -fu_cab_get_vendor (FuCab *cab) -{ - FuCabPrivate *priv = GET_PRIVATE (cab); - g_return_val_if_fail (FU_IS_CAB (cab), NULL); - return priv->vendor; -} - -/** - * fu_cab_get_name: - **/ -const gchar * -fu_cab_get_name (FuCab *cab) -{ - FuCabPrivate *priv = GET_PRIVATE (cab); - g_return_val_if_fail (FU_IS_CAB (cab), NULL); - return priv->name; -} - -/** - * fu_cab_get_summary: - **/ -const gchar * -fu_cab_get_summary (FuCab *cab) -{ - FuCabPrivate *priv = GET_PRIVATE (cab); - g_return_val_if_fail (FU_IS_CAB (cab), NULL); - return priv->summary; -} - -/** - * fu_cab_get_description: - **/ -const gchar * -fu_cab_get_description (FuCab *cab) -{ - FuCabPrivate *priv = GET_PRIVATE (cab); - g_return_val_if_fail (FU_IS_CAB (cab), NULL); - return priv->description; -} - -/** - * fu_cab_get_url_homepage: - **/ -const gchar * -fu_cab_get_url_homepage (FuCab *cab) -{ - FuCabPrivate *priv = GET_PRIVATE (cab); - g_return_val_if_fail (FU_IS_CAB (cab), NULL); - return priv->url_homepage; -} - -/** - * fu_cab_get_size: - **/ -guint64 -fu_cab_get_size (FuCab *cab) -{ - FuCabPrivate *priv = GET_PRIVATE (cab); - g_return_val_if_fail (FU_IS_CAB (cab), 0); - return priv->size; -} - -/** - * fu_cab_get_license: - **/ -const gchar * -fu_cab_get_license (FuCab *cab) -{ - FuCabPrivate *priv = GET_PRIVATE (cab); - g_return_val_if_fail (FU_IS_CAB (cab), NULL); - return priv->license; -} - -/** - * fu_cab_get_filename_firmware: - **/ -const gchar * -fu_cab_get_filename_firmware (FuCab *cab) -{ - FuCabPrivate *priv = GET_PRIVATE (cab); - g_return_val_if_fail (FU_IS_CAB (cab), NULL); - return priv->firmware_filename; -} - -/** - * fu_cab_get_trust_flags: - **/ -FwupdTrustFlags -fu_cab_get_trust_flags (FuCab *cab) -{ - FuCabPrivate *priv = GET_PRIVATE (cab); - g_return_val_if_fail (FU_IS_CAB (cab), 0); - return priv->trust_flags; -} - -/** - * fu_cab_class_init: - **/ -static void -fu_cab_class_init (FuCabClass *klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS (klass); - object_class->finalize = fu_cab_finalize; -} - -/** - * fu_cab_init: - **/ -static void -fu_cab_init (FuCab *cab) -{ - FuCabPrivate *priv = GET_PRIVATE (cab); - priv = GET_PRIVATE (cab); - priv->basenames_to_delete = g_ptr_array_new_with_free_func (g_free); - priv->filelist = g_ptr_array_new_with_free_func (g_free); -} - -/** - * fu_cab_finalize: - **/ -static void -fu_cab_finalize (GObject *object) -{ - FuCab *cab = FU_CAB (object); - FuCabPrivate *priv = GET_PRIVATE (cab); - - g_free (priv->firmware_basename); - g_free (priv->firmware_filename); - g_free (priv->signature_basename); - g_free (priv->cat_basename); - g_free (priv->guid); - g_free (priv->inf_basename); - g_free (priv->metainfo_basename); - g_free (priv->tmp_path); - g_free (priv->name); - g_free (priv->summary); - g_free (priv->description); - g_free (priv->url_homepage); - g_free (priv->vendor); - g_free (priv->version); - g_free (priv->license); - if (priv->cab_stream != NULL) - g_object_unref (priv->cab_stream); - if (priv->gcab != NULL) - g_object_unref (priv->gcab); - if (priv->inf_kf != NULL) - g_key_file_unref (priv->inf_kf); - g_ptr_array_unref (priv->basenames_to_delete); - g_ptr_array_unref (priv->filelist); - - G_OBJECT_CLASS (fu_cab_parent_class)->finalize (object); -} - -/** - * fu_cab_new: - **/ -FuCab * -fu_cab_new (void) -{ - FuCab *cab; - cab = g_object_new (FU_TYPE_CAB, NULL); - return FU_CAB (cab); -} diff --git a/src/fu-cab.h b/src/fu-cab.h deleted file mode 100644 index 5e22a6057..000000000 --- a/src/fu-cab.h +++ /dev/null @@ -1,88 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- - * - * Copyright (C) 2015 Richard Hughes - * - * Licensed under the GNU General Public License Version 2 - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#ifndef __FU_CAB_H -#define __FU_CAB_H - -#include -#include - -G_BEGIN_DECLS - -#define FU_TYPE_CAB (fu_cab_get_type ()) -G_DECLARE_DERIVABLE_TYPE (FuCab, fu_cab, FU, CAB, GObject) - -struct _FuCabClass -{ - GObjectClass parent_class; -}; - -typedef enum { - FU_CAB_EXTRACT_FLAG_UNKNOWN = 0, - FU_CAB_EXTRACT_FLAG_INF = 1, - FU_CAB_EXTRACT_FLAG_METAINFO = 2, - FU_CAB_EXTRACT_FLAG_FIRMWARE = 4, - FU_CAB_EXTRACT_FLAG_SIGNATURE = 8, - FU_CAB_EXTRACT_FLAG_CATALOG = 16, - FU_CAB_EXTRACT_FLAG_ALL = 0xff, - FU_CAB_EXTRACT_FLAG_LAST -} FuCabExtractFlags; - -FuCab *fu_cab_new (void); - -gboolean fu_cab_verify (FuCab *cab, - GError **error); -gboolean fu_cab_load_fd (FuCab *cab, - gint fd, - GCancellable *cancellable, - GError **error); -gboolean fu_cab_load_file (FuCab *cab, - GFile *file, - GCancellable *cancellable, - GError **error); -gboolean fu_cab_save_file (FuCab *cab, - GFile *file, - GCancellable *cancellable, - GError **error); -gboolean fu_cab_extract (FuCab *cab, - FuCabExtractFlags flags, - GError **error); -gboolean fu_cab_delete_temp_files (FuCab *cab, - GError **error); -GInputStream *fu_cab_get_stream (FuCab *cab); -const gchar *fu_cab_get_guid (FuCab *cab); -const gchar *fu_cab_get_version (FuCab *cab); -const gchar *fu_cab_get_vendor (FuCab *cab); -const gchar *fu_cab_get_summary (FuCab *cab); -const gchar *fu_cab_get_name (FuCab *cab); -const gchar *fu_cab_get_description (FuCab *cab); -const gchar *fu_cab_get_license (FuCab *cab); -const gchar *fu_cab_get_url_homepage (FuCab *cab); -const gchar *fu_cab_get_filename_firmware (FuCab *cab); -guint64 fu_cab_get_size (FuCab *cab); -FwupdTrustFlags fu_cab_get_trust_flags (FuCab *cab); -void fu_cab_add_file (FuCab *cab, - const gchar *filename); - -G_END_DECLS - -#endif /* __FU_CAB_H */ - diff --git a/src/fu-keyring.c b/src/fu-keyring.c index ae5c26b33..58a6e25c5 100644 --- a/src/fu-keyring.c +++ b/src/fu-keyring.c @@ -99,95 +99,6 @@ fu_keyring_setup (FuKeyring *keyring, GError **error) return TRUE; } -/** - * fu_keyring_list_private_keys: - **/ -static void -fu_keyring_list_private_keys (FuKeyring *keyring) -{ - FuKeyringPrivate *priv = GET_PRIVATE (keyring); - gpgme_key_t key; - gpgme_error_t err; - - err = gpgme_op_keylist_start (priv->ctx, NULL, 1); - while (!err) { - g_autoptr(GString) str = NULL; - err = gpgme_op_keylist_next (priv->ctx, &key); - if (err) - break; - str = g_string_new (key->subkeys->keyid); - g_string_append_printf (str, "\t[secret:%i, sign:%i]", - key->subkeys->secret, - key->subkeys->can_sign); - if (key->uids && key->uids->name) - g_string_append_printf (str, " %s", key->uids->name); - if (key->uids && key->uids->email) - g_string_append_printf (str, " <%s>", key->uids->email); - g_debug ("%s", str->str); - gpgme_key_release (key); - } - - if (gpg_err_code (err) != GPG_ERR_EOF) { - g_warning ("can not list keys: %s\n", gpgme_strerror (err)); - return; - } -} - -/** - * fu_keyring_set_signing_key: - **/ -gboolean -fu_keyring_set_signing_key (FuKeyring *keyring, const gchar *key_id, GError **error) -{ - FuKeyringPrivate *priv = GET_PRIVATE (keyring); - gint n_signers; - gpgme_error_t rc; - gpgme_key_t key; - - - /* setup context */ - if (!fu_keyring_setup (keyring, error)) - return FALSE; - - /* list possible keys */ - fu_keyring_list_private_keys (keyring); - - /* find key */ - rc = gpgme_get_key (priv->ctx, key_id, &key, 1); - if (rc != GPG_ERR_NO_ERROR) { - g_set_error (error, - FWUPD_ERROR, - FWUPD_ERROR_INTERNAL, - "failed to find key %s: %s", - key_id, gpgme_strerror (rc)); - return FALSE; - } - - /* select it to be used */ - gpgme_signers_clear (priv->ctx); - rc = gpgme_signers_add (priv->ctx, key); - if (rc != GPG_ERR_NO_ERROR) { - g_set_error (error, - FWUPD_ERROR, - FWUPD_ERROR_INTERNAL, - "failed to add signing key %s: %s", - key_id, gpgme_strerror (rc)); - return FALSE; - } - gpgme_key_unref (key); - - /* check it's selected */ - n_signers = gpgme_signers_count (priv->ctx); - if (n_signers != 1) { - g_set_error (error, - FWUPD_ERROR, - FWUPD_ERROR_INTERNAL, - "failed to check signing key %s", key_id); - return FALSE; - } - return TRUE; -} - /** * fu_keyring_add_public_key: **/ @@ -423,76 +334,6 @@ out: return ret; } -/** - * fu_keyring_sign_data: - **/ -GBytes * -fu_keyring_sign_data (FuKeyring *keyring, GBytes *payload, GError **error) -{ - FuKeyringPrivate *priv = GET_PRIVATE (keyring); - GBytes *sig_bytes = NULL; - gchar *sig_data = NULL; - gpgme_data_t data = NULL; - gpgme_data_t sig = NULL; - gpgme_error_t rc; - gpgme_sign_result_t sign_result; - gsize sig_len = 0; - - g_return_val_if_fail (FU_IS_KEYRING (keyring), NULL); - g_return_val_if_fail (payload != NULL, NULL); - - /* setup context */ - if (!fu_keyring_setup (keyring, error)) - return NULL; - - /* load file data */ - rc = gpgme_data_new_from_mem (&data, - g_bytes_get_data (payload, NULL), - g_bytes_get_size (payload), 0); - if (rc != GPG_ERR_NO_ERROR) { - g_set_error (error, - FWUPD_ERROR, - FWUPD_ERROR_INTERNAL, - "failed to load data: %s", - gpgme_strerror (rc)); - goto out; - } - - /* sign */ - gpgme_data_new (&sig); - rc = gpgme_op_sign (priv->ctx, data, sig, GPGME_SIG_MODE_DETACH); - if (rc != GPG_ERR_NO_ERROR) { - g_set_error (error, - FWUPD_ERROR, - FWUPD_ERROR_INTERNAL, - "failed to sign data: %s", - gpgme_strerror (rc)); - goto out; - } - sign_result = gpgme_op_sign_result (priv->ctx); - if (sign_result == NULL || - sign_result->signatures == NULL || - sign_result->signatures->fpr == NULL) { - g_set_error (error, - FWUPD_ERROR, - FWUPD_ERROR_INTERNAL, - "failed to sign data with any key"); - goto out; - } - g_debug ("signed with key %s", sign_result->signatures->fpr); - - /* steal signature data */ - sig_data = gpgme_data_release_and_get_mem (sig, &sig_len); - sig_bytes = g_bytes_new_with_free_func (sig_data, sig_len, (GDestroyNotify) gpgme_free, sig_data); - sig = NULL; -out: - if (data != NULL) - gpgme_data_release (data); - if (sig != NULL) - gpgme_data_release (sig); - return sig_bytes; -} - /** * fu_keyring_verify_data: **/ diff --git a/src/fu-keyring.h b/src/fu-keyring.h index 31a51509c..e0ca6ea1c 100644 --- a/src/fu-keyring.h +++ b/src/fu-keyring.h @@ -43,9 +43,6 @@ gboolean fu_keyring_add_public_keys (FuKeyring *keyring, gboolean fu_keyring_add_public_key (FuKeyring *keyring, const gchar *filename, GError **error); -gboolean fu_keyring_set_signing_key (FuKeyring *keyring, - const gchar *key_id, - GError **error); gboolean fu_keyring_verify_file (FuKeyring *keyring, const gchar *filename, const gchar *signature, @@ -54,9 +51,6 @@ gboolean fu_keyring_verify_data (FuKeyring *keyring, GBytes *payload, GBytes *payload_signature, GError **error); -GBytes *fu_keyring_sign_data (FuKeyring *keyring, - GBytes *payload, - GError **error); G_END_DECLS diff --git a/src/fu-main.c b/src/fu-main.c index fb0b89680..97537c527 100644 --- a/src/fu-main.c +++ b/src/fu-main.c @@ -33,7 +33,6 @@ #include #include -#include "fu-cab.h" #include "fu-cleanup.h" #include "fu-debug.h" #include "fu-device.h" @@ -52,12 +51,14 @@ #include "fu-provider-uefi.h" #endif +#define FU_MAIN_FIRMWARE_SIZE_MAX (32 * 1024 * 1024) /* bytes */ + typedef struct { GDBusConnection *connection; GDBusNodeInfo *introspection_daemon; GDBusProxy *proxy_uid; GMainLoop *loop; - GPtrArray *devices; + GPtrArray *devices; /* of FuDeviceItem */ GPtrArray *providers; PolkitAuthority *authority; FwupdStatus status; @@ -197,23 +198,6 @@ fu_main_get_item_by_id (FuMainPrivate *priv, const gchar *id) return NULL; } -/** - * fu_main_get_item_by_guid: - **/ -static FuDeviceItem * -fu_main_get_item_by_guid (FuMainPrivate *priv, const gchar *guid) -{ - FuDeviceItem *item; - guint i; - - for (i = 0; i < priv->devices->len; i++) { - item = g_ptr_array_index (priv->devices, i); - if (g_strcmp0 (fu_device_get_guid (item->device), guid) == 0) - return item; - } - return NULL; -} - /** * fu_main_get_provider_by_name: **/ @@ -231,14 +215,86 @@ fu_main_get_provider_by_name (FuMainPrivate *priv, const gchar *name) return NULL; } +/** + * fu_main_get_release_trust_flags: + **/ +static gboolean +fu_main_get_release_trust_flags (AsRelease *release, + FwupdTrustFlags *trust_flags, + GError **error) +{ + AsChecksum *csum_tmp; + GBytes *blob_payload; + GBytes *blob_signature; + const gchar *fn; + g_autofree gchar *pki_dir = NULL; + g_autofree gchar *fn_signature = NULL; + g_autoptr(GError) error_local = NULL; + g_autoptr(FuKeyring) kr = NULL; + + /* no filename? */ + csum_tmp = as_release_get_checksum_by_target (release, AS_CHECKSUM_TARGET_CONTENT); + fn = as_checksum_get_filename (csum_tmp); + if (fn == NULL) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "no filename"); + return FALSE; + } + + /* no signature == no trust */ + fn_signature = g_strdup_printf ("%s.asc", fn); + blob_signature = as_release_get_blob (release, fn_signature); + if (blob_signature == NULL) { + g_debug ("firmware archive contained no GPG signature"); + return TRUE; + } + + /* get payload */ + blob_payload = as_release_get_blob (release, fn); + if (blob_payload == NULL) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "no payload"); + return FALSE; + } + + /* check we were installed correctly */ + pki_dir = g_build_filename (SYSCONFDIR, "pki", "fwupd", NULL); + if (!g_file_test (pki_dir, G_FILE_TEST_EXISTS)) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_FOUND, + "PKI directory %s not found", pki_dir); + return FALSE; + } + + /* verify against the system trusted keys */ + kr = fu_keyring_new (); + if (!fu_keyring_add_public_keys (kr, pki_dir, error)) + return FALSE; + if (!fu_keyring_verify_data (kr, blob_payload, blob_signature, &error_local)) { + g_warning ("untrusted as failed to verify: %s", + error_local->message); + return TRUE; + } + + /* awesome! */ + g_debug ("marking payload as trusted"); + *trust_flags |= FWUPD_TRUST_FLAG_PAYLOAD; + return TRUE; +} + typedef struct { GDBusMethodInvocation *invocation; - FuCab *cab; + AsStore *store; + FwupdTrustFlags trust_flags; FuDevice *device; FuProviderFlags flags; - gchar *id; - gint firmware_fd; - gint cab_fd; + GBytes *blob_fw; + GBytes *blob_cab; gint vercmp; FuMainPrivate *priv; } FuMainAuthHelper; @@ -249,21 +305,15 @@ typedef struct { static void fu_main_helper_free (FuMainAuthHelper *helper) { - /* delete temp files */ - fu_cab_delete_temp_files (helper->cab, NULL); - g_object_unref (helper->cab); - - /* close any open files */ - if (helper->cab_fd > 0) - close (helper->cab_fd); - if (helper->firmware_fd > 0) - close (helper->firmware_fd); - /* free */ - g_free (helper->id); if (helper->device != NULL) g_object_unref (helper->device); + if (helper->blob_fw > 0) + g_bytes_unref (helper->blob_fw); + if (helper->blob_cab > 0) + g_bytes_unref (helper->blob_cab); g_object_unref (helper->invocation); + g_object_unref (helper->store); g_free (helper); } @@ -289,8 +339,8 @@ fu_main_provider_update_authenticated (FuMainAuthHelper *helper, GError **error) /* run the correct provider that added this */ return fu_provider_update (item->provider, item->device, - fu_cab_get_stream (helper->cab), - helper->firmware_fd, + helper->blob_cab, + helper->blob_fw, helper->flags, error); } @@ -340,49 +390,124 @@ fu_main_check_authorization_cb (GObject *source, GAsyncResult *res, gpointer use fu_main_helper_free (helper); } +/** + * fu_main_get_guids_from_store: + **/ +static gchar * +fu_main_get_guids_from_store (AsStore *store) +{ + AsApp *app; + AsProvide *prov; + GPtrArray *provides; + GPtrArray *apps; + GString *str = g_string_new (""); + guint i; + guint j; + + /* return a string with all the firmware apps in the store */ + apps = as_store_get_apps (store); + for (i = 0; i < apps->len; i++) { + app = AS_APP (g_ptr_array_index (apps, i)); + provides = as_app_get_provides (app); + for (j = 0; j < provides->len; j++) { + prov = AS_PROVIDE (g_ptr_array_index (provides, j)); + if (as_provide_get_kind (prov) != AS_PROVIDE_KIND_FIRMWARE_FLASHED) + continue; + g_string_append_printf (str, "%s,", as_provide_get_value (prov)); + } + } + if (str->len == 0) + return NULL; + g_string_truncate (str, str->len - 1); + return g_string_free (str, FALSE); +} + /** * fu_main_update_helper: **/ static gboolean fu_main_update_helper (FuMainAuthHelper *helper, GError **error) { - const gchar *guid; + AsApp *app; + AsChecksum *csum_tmp; + AsRelease *rel; const gchar *tmp; const gchar *version; + guint i; - /* load cab file */ - fu_main_set_status (helper->priv, FWUPD_STATUS_LOADING); - if (!fu_cab_load_fd (helper->cab, helper->cab_fd, NULL, error)) + /* load store file which also decompresses firmware */ + fu_main_set_status (helper->priv, FWUPD_STATUS_DECOMPRESSING); + if (!as_store_from_bytes (helper->store, helper->blob_cab, NULL, error)) return FALSE; - /* are we matching *any* hardware */ - guid = fu_cab_get_guid (helper->cab); + /* if we've not chosen a device, try and find anything in the + * cabinet 'store' that matches any installed device */ if (helper->device == NULL) { - FuDeviceItem *item; - item = fu_main_get_item_by_guid (helper->priv, guid); - if (item == NULL) { + for (i = 0; i < helper->priv->devices->len; i++) { + FuDeviceItem *item; + item = g_ptr_array_index (helper->priv->devices, i); + app = as_store_get_app_by_provide (helper->store, + AS_PROVIDE_KIND_FIRMWARE_FLASHED, + fu_device_get_guid (item->device)); + if (app != NULL) { + helper->device = g_object_ref (item->device); + break; + } + } + + /* nothing found */ + if (helper->device == NULL) { + g_autofree gchar *guid = NULL; + guid = fu_main_get_guids_from_store (helper->store); g_set_error (error, FWUPD_ERROR, FWUPD_ERROR_INVALID_FILE, - "no hardware matched %s", + "no attached hardware matched %s", guid); return FALSE; } - helper->device = g_object_ref (item->device); - } - - tmp = fu_device_get_guid (helper->device); - if (g_strcmp0 (guid, tmp) != 0) { - g_set_error (error, - FWUPD_ERROR, - FWUPD_ERROR_INVALID_FILE, - "firmware is not for this hw: required %s got %s", - tmp, guid); - return FALSE; + } else { + /* find an application from the cabinet 'store' for the + * chosen device */ + app = as_store_get_app_by_provide (helper->store, + AS_PROVIDE_KIND_FIRMWARE_FLASHED, + fu_device_get_guid (helper->device)); + if (app == NULL) { + g_autofree gchar *guid = NULL; + guid = fu_main_get_guids_from_store (helper->store); + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "firmware is not for this hw: required %s got %s", + fu_device_get_guid (helper->device), guid); + return FALSE; + } } /* parse the DriverVer */ - version = fu_cab_get_version (helper->cab); + rel = as_app_get_release_default (app); + if (rel == NULL) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "no releases in the firmware component"); + return FALSE; + } + + /* get the blob */ + csum_tmp = as_release_get_checksum_by_target (rel, AS_CHECKSUM_TARGET_CONTENT); + tmp = as_checksum_get_filename (csum_tmp); + g_assert (tmp != NULL); + helper->blob_fw = as_release_get_blob (rel, tmp); + if (helper->blob_fw == NULL) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_READ, + "failed to get firmware blob"); + return FALSE; + } + + version = as_release_get_version (rel); fu_device_set_metadata (helper->device, FU_DEVICE_KEY_UPDATE_VERSION, version); /* compare to the lowest supported version, if it exists */ @@ -424,25 +549,9 @@ fu_main_update_helper (FuMainAuthHelper *helper, GError **error) return FALSE; } - /* now extract the firmware and set any trust flags */ - fu_main_set_status (helper->priv, FWUPD_STATUS_DECOMPRESSING); - if (!fu_cab_extract (helper->cab, FU_CAB_EXTRACT_FLAG_FIRMWARE | - FU_CAB_EXTRACT_FLAG_SIGNATURE, error)) + /* verify */ + if (!fu_main_get_release_trust_flags (rel, &helper->trust_flags, error)) return FALSE; - if (!fu_cab_verify (helper->cab, error)) - return FALSE; - - /* and open it */ - helper->firmware_fd = g_open (fu_cab_get_filename_firmware (helper->cab), - O_CLOEXEC, 0); - if (helper->firmware_fd < 0) { - g_set_error (error, - FWUPD_ERROR, - FWUPD_ERROR_READ, - "failed to open %s", - fu_cab_get_filename_firmware (helper->cab)); - return FALSE; - } return TRUE; } @@ -555,7 +664,7 @@ fu_main_get_action_id_for_device (FuMainAuthHelper *helper) gboolean is_downgrade; /* only test the payload */ - is_trusted = (fu_cab_get_trust_flags (helper->cab) & FWUPD_TRUST_FLAG_PAYLOAD) > 0; + is_trusted = (helper->trust_flags & FWUPD_TRUST_FLAG_PAYLOAD) > 0; is_downgrade = helper->vercmp > 0; /* relax authentication checks for removable devices */ @@ -1045,6 +1154,8 @@ fu_main_daemon_method_call (GDBusConnection *connection, const gchar *sender, g_autoptr(GError) error = NULL; _cleanup_object_unref_ PolkitSubject *subject = NULL; g_autoptr(GVariantIter) iter = NULL; + g_autoptr(GBytes) blob_cab = NULL; + g_autoptr(GInputStream) stream = NULL; /* check the id exists */ g_variant_get (parameters, "(&sha{sv})", &id, &fd_handle, &iter); @@ -1094,14 +1205,25 @@ fu_main_daemon_method_call (GDBusConnection *connection, const gchar *sender, return; } + /* read the entire fd to a data blob */ + stream = g_unix_input_stream_new (fd, TRUE); + blob_cab = g_input_stream_read_bytes (stream, + FU_MAIN_FIRMWARE_SIZE_MAX, + NULL, &error); + if (blob_cab == NULL){ + g_dbus_method_invocation_return_gerror (invocation, + error); + return; + } + /* process the firmware */ helper = g_new0 (FuMainAuthHelper, 1); helper->invocation = g_object_ref (invocation); - helper->cab_fd = fd; - helper->id = g_strdup (id); + helper->trust_flags = FWUPD_TRUST_FLAG_NONE; + helper->blob_cab = g_bytes_ref (blob_cab); helper->flags = flags; helper->priv = priv; - helper->cab = fu_cab_new (); + helper->store = as_store_new (); if (item != NULL) helper->device = g_object_ref (item->device); if (!fu_main_update_helper (helper, &error)) { @@ -1139,15 +1261,23 @@ fu_main_daemon_method_call (GDBusConnection *connection, const gchar *sender, /* return 'a{sv}' */ if (g_strcmp0 (method_name, "GetDetails") == 0) { + AsApp *app = NULL; + AsRelease *rel; GDBusMessage *message; + GPtrArray *apps; + GPtrArray *provides; GUnixFDList *fd_list; GVariantBuilder builder; - FwupdTrustFlags trust_flags; + FwupdTrustFlags trust_flags = FWUPD_TRUST_FLAG_NONE; const gchar *tmp; + const gchar *guid = NULL; gint32 fd_handle = 0; + guint i; gint fd; + g_autoptr(AsStore) store = NULL; + g_autoptr(GBytes) blob_cab = NULL; g_autoptr(GError) error = NULL; - g_autoptr(FuCab) cab = NULL; + g_autoptr(GInputStream) stream = NULL; /* check the id exists */ g_variant_get (parameters, "(h)", &fd_handle); @@ -1170,19 +1300,71 @@ fu_main_daemon_method_call (GDBusConnection *connection, const gchar *sender, return; } - /* load file */ - cab = fu_cab_new (); - if (!fu_cab_load_fd (cab, fd, NULL, &error)) { - g_dbus_method_invocation_return_gerror (invocation, error); + /* read the entire fd to a data blob */ + stream = g_unix_input_stream_new (fd, TRUE); + blob_cab = g_input_stream_read_bytes (stream, + FU_MAIN_FIRMWARE_SIZE_MAX, + NULL, &error); + if (blob_cab == NULL){ + g_dbus_method_invocation_return_gerror (invocation, + error); return; } - if (!fu_cab_delete_temp_files (cab, &error)) { + + /* load file */ + store = as_store_new (); + if (!as_store_from_bytes (store, blob_cab, NULL, &error)) { g_dbus_method_invocation_return_gerror (invocation, error); return; } - /* we have to extract the firmware to check the signature */ - if (!fu_cab_extract (cab, FU_CAB_EXTRACT_FLAG_FIRMWARE, &error)) { + /* get default app */ + apps = as_store_get_apps (store); + if (apps->len == 0) { + g_dbus_method_invocation_return_error (invocation, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "no components"); + return; + } + if (apps->len > 1) { + /* we've got a .cab file with multiple components, + * so try to find the first thing that's installed */ + for (i = 0; i < priv->devices->len; i++) { + FuDeviceItem *item; + item = g_ptr_array_index (priv->devices, i); + app = as_store_get_app_by_provide (store, + AS_PROVIDE_KIND_FIRMWARE_FLASHED, + fu_device_get_guid (item->device)); + if (app != NULL) + break; + } + } + + /* well, we've tried our best, just show the first entry */ + if (app == NULL) + app = AS_APP (g_ptr_array_index (apps, 0)); + + /* get guid */ + provides = as_app_get_provides (app); + for (i = 0; i < provides->len; i++) { + AsProvide *prov = AS_PROVIDE (g_ptr_array_index (provides, i)); + if (as_provide_get_kind (prov) == AS_PROVIDE_KIND_FIRMWARE_FLASHED) { + guid = as_provide_get_value (prov); + break; + } + } + if (guid == NULL) { + g_dbus_method_invocation_return_error (invocation, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "component has no GUID"); + return; + } + + /* verify trust */ + rel = as_app_get_release_default (app); + if (!fu_main_get_release_trust_flags (rel, &trust_flags, &error)) { g_dbus_method_invocation_return_gerror (invocation, error); return; } @@ -1191,52 +1373,51 @@ fu_main_daemon_method_call (GDBusConnection *connection, const gchar *sender, g_variant_builder_init (&builder, G_VARIANT_TYPE_ARRAY); g_variant_builder_add (&builder, "{sv}", FU_DEVICE_KEY_VERSION, - g_variant_new_string (fu_cab_get_version (cab))); + g_variant_new_string (as_release_get_version (rel))); g_variant_builder_add (&builder, "{sv}", FU_DEVICE_KEY_GUID, - g_variant_new_string (fu_cab_get_guid (cab))); + g_variant_new_string (guid)); g_variant_builder_add (&builder, "{sv}", FU_DEVICE_KEY_SIZE, - g_variant_new_uint64 (fu_cab_get_size (cab))); + g_variant_new_uint64 (as_release_get_size (rel, AS_SIZE_KIND_INSTALLED))); /* optional properties */ - tmp = fu_cab_get_vendor (cab); + tmp = as_app_get_developer_name (app, NULL); if (tmp != NULL) { g_variant_builder_add (&builder, "{sv}", FU_DEVICE_KEY_VENDOR, g_variant_new_string (tmp)); } - tmp = fu_cab_get_name (cab); + tmp = as_app_get_name (app, NULL); if (tmp != NULL) { g_variant_builder_add (&builder, "{sv}", FU_DEVICE_KEY_NAME, g_variant_new_string (tmp)); } - tmp = fu_cab_get_summary (cab); + tmp = as_app_get_comment (app, NULL); if (tmp != NULL) { g_variant_builder_add (&builder, "{sv}", FU_DEVICE_KEY_SUMMARY, g_variant_new_string (tmp)); } - tmp = fu_cab_get_description (cab); + tmp = as_app_get_description (app, NULL); if (tmp != NULL) { g_variant_builder_add (&builder, "{sv}", FU_DEVICE_KEY_DESCRIPTION, g_variant_new_string (tmp)); } - tmp = fu_cab_get_url_homepage (cab); + tmp = as_app_get_url_item (app, AS_URL_KIND_HOMEPAGE); if (tmp != NULL) { g_variant_builder_add (&builder, "{sv}", FU_DEVICE_KEY_URL_HOMEPAGE, g_variant_new_string (tmp)); } - tmp = fu_cab_get_license (cab); + tmp = as_app_get_project_license (app); if (tmp != NULL) { g_variant_builder_add (&builder, "{sv}", FU_DEVICE_KEY_LICENSE, g_variant_new_string (tmp)); } - trust_flags = fu_cab_get_trust_flags (cab); g_variant_builder_add (&builder, "{sv}", FU_DEVICE_KEY_TRUSTED, g_variant_new_uint64 (trust_flags)); diff --git a/src/fu-provider-chug.c b/src/fu-provider-chug.c index 7fd398456..b429fa225 100644 --- a/src/fu-provider-chug.c +++ b/src/fu-provider-chug.c @@ -300,14 +300,13 @@ fu_provider_chug_verify (FuProvider *provider, static gboolean fu_provider_chug_update (FuProvider *provider, FuDevice *device, - gint fd, + GBytes *blob_fw, FuProviderFlags flags, GError **error) { FuProviderChug *provider_chug = FU_PROVIDER_CHUG (provider); FuProviderChugPrivate *priv = GET_PRIVATE (provider_chug); FuProviderChugItem *item; - g_autoptr(GInputStream) stream = NULL; g_autoptr(GError) error_local = NULL; /* find item */ @@ -323,12 +322,7 @@ fu_provider_chug_update (FuProvider *provider, } /* this file is so small, just slurp it all in one go */ - stream = g_unix_input_stream_new (fd, TRUE); - item->fw_bin = g_input_stream_read_bytes (stream, - FU_PROVIDER_CHUG_FIRMWARE_MAX, - NULL, error); - if (item->fw_bin == NULL) - return FALSE; + item->fw_bin = g_bytes_ref (blob_fw); /* check this firmware is actually for this device */ if (!ch_device_check_firmware (item->usb_device, @@ -461,10 +455,6 @@ fu_provider_chug_update (FuProvider *provider, return FALSE; } - /* close stream */ - if (!g_input_stream_close (stream, NULL, error)) - return FALSE; - /* get the new firmware version */ g_debug ("ColorHug: Getting new firmware version"); item->got_version = FALSE; diff --git a/src/fu-provider-fake.c b/src/fu-provider-fake.c index 14c27801f..1cc5a5d98 100644 --- a/src/fu-provider-fake.c +++ b/src/fu-provider-fake.c @@ -47,7 +47,7 @@ fu_provider_fake_get_name (FuProvider *provider) static gboolean fu_provider_fake_update (FuProvider *provider, FuDevice *device, - gint fd, + GBytes *blob_fw, FuProviderFlags flags, GError **error) { diff --git a/src/fu-provider-rpi.c b/src/fu-provider-rpi.c index 3499a88bd..ea3596601 100644 --- a/src/fu-provider-rpi.c +++ b/src/fu-provider-rpi.c @@ -195,7 +195,7 @@ fu_provider_rpi_explode_file (struct archive_entry *entry, const gchar *dir) static gboolean fu_provider_rpi_update (FuProvider *provider, FuDevice *device, - gint fd, + GBytes *blob_fw, FuProviderFlags flags, GError **error) { @@ -213,7 +213,9 @@ fu_provider_rpi_update (FuProvider *provider, arch = archive_read_new (); archive_read_support_format_all (arch); archive_read_support_filter_all (arch); - r = archive_read_open_fd (arch, fd, 1024 * 32); + r = archive_read_open_memory (arch, + (void *) g_bytes_get_data (blob_fw, NULL), + (size_t) g_bytes_get_size (blob_fw)); if (r) { ret = FALSE; g_set_error (error, diff --git a/src/fu-provider-uefi.c b/src/fu-provider-uefi.c index 2f97c65d3..b2bca0a27 100644 --- a/src/fu-provider-uefi.c +++ b/src/fu-provider-uefi.c @@ -25,7 +25,9 @@ #include #include #include +#include #include +#include #include "fu-device.h" #include "fu-pending.h" @@ -206,14 +208,40 @@ out: static gboolean fu_provider_uefi_update (FuProvider *provider, FuDevice *device, - gint fd, + GBytes *blob_fw, FuProviderFlags flags, GError **error) { + g_autoptr(GError) error_local = NULL; fwup_resource_iter *iter = NULL; fwup_resource *re = NULL; gboolean ret = TRUE; guint64 hardware_instance = 0; /* FIXME */ + int fd; + const gchar *fn = "/boot/fwupd-efiupdate"; + + /* save the data to a temp file */ + if (!g_file_set_contents (fn, + g_bytes_get_data (blob_fw, NULL), + g_bytes_get_size (blob_fw), + &error_local)) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_WRITE, + "Failed to write temp file: %s", + error_local->message); + return FALSE; + } + + /* open the file */ + fd = g_open (fn, O_CLOEXEC, 0); + if (fd < 0) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_READ, + "failed to open %s", fn); + return FALSE; + } /* get the hardware we're referencing */ fwup_resource_iter_create (&iter); diff --git a/src/fu-provider.c b/src/fu-provider.c index 4f5a5e51d..e023c9d84 100644 --- a/src/fu-provider.c +++ b/src/fu-provider.c @@ -35,8 +35,6 @@ static void fu_provider_finalize (GObject *object); -#define FU_PROVIDER_FIRMWARE_MAX (32 * 1024 * 1024) /* bytes */ - enum { SIGNAL_DEVICE_ADDED, SIGNAL_DEVICE_REMOVED, @@ -116,12 +114,11 @@ fu_provider_coldplug (FuProvider *provider, GError **error) static gboolean fu_provider_schedule_update (FuProvider *provider, FuDevice *device, - GInputStream *stream, + GBytes *blob_cab, GError **error) { gchar tmpname[] = {"XXXXXX.cap"}; guint i; - g_autoptr(GBytes) fwbin = NULL; g_autofree gchar *dirname = NULL; g_autofree gchar *filename = NULL; g_autoptr(FuDevice) device_tmp = NULL; @@ -155,16 +152,9 @@ fu_provider_schedule_update (FuProvider *provider, /* just copy to the temp file */ fu_provider_set_status (provider, FWUPD_STATUS_SCHEDULING); - if (!g_seekable_seek (G_SEEKABLE (stream), 0, G_SEEK_SET, NULL, error)) - return FALSE; - fwbin = g_input_stream_read_bytes (stream, - FU_PROVIDER_FIRMWARE_MAX, - NULL, error); - if (fwbin == NULL) - return FALSE; if (!g_file_set_contents (filename, - g_bytes_get_data (fwbin, NULL), - g_bytes_get_size (fwbin), + g_bytes_get_data (blob_cab, NULL), + g_bytes_get_size (blob_cab), error)) return FALSE; @@ -202,8 +192,8 @@ fu_provider_verify (FuProvider *provider, gboolean fu_provider_update (FuProvider *provider, FuDevice *device, - GInputStream *stream_cab, - gint fd_fw, + GBytes *blob_cab, + GBytes *blob_fw, FuProviderFlags flags, GError **error) { @@ -217,9 +207,9 @@ fu_provider_update (FuProvider *provider, if (klass->update_offline == NULL) return fu_provider_schedule_update (provider, device, - stream_cab, + blob_cab, error); - return klass->update_offline (provider, device, fd_fw, flags, error); + return klass->update_offline (provider, device, blob_fw, flags, error); } /* cancel the pending action */ @@ -236,7 +226,7 @@ fu_provider_update (FuProvider *provider, } pending = fu_pending_new (); device_pending = fu_pending_get_device (pending, fu_device_get_id (device), NULL); - if (!klass->update_online (provider, device, fd_fw, flags, &error_update)) { + if (!klass->update_online (provider, device, blob_fw, flags, &error_update)) { /* save the error to the database */ if (device_pending != NULL) { fu_pending_set_error_msg (pending, device, diff --git a/src/fu-provider.h b/src/fu-provider.h index 6ab719b34..8536695b7 100644 --- a/src/fu-provider.h +++ b/src/fu-provider.h @@ -59,12 +59,12 @@ struct _FuProviderClass GError **error); gboolean (*update_online) (FuProvider *provider, FuDevice *device, - gint fd, + GBytes *blob_fw, FuProviderFlags flags, GError **error); gboolean (*update_offline) (FuProvider *provider, FuDevice *device, - gint fd, + GBytes *blob_fw, FuProviderFlags flags, GError **error); gboolean (*clear_results) (FuProvider *provider, @@ -96,8 +96,8 @@ gboolean fu_provider_coldplug (FuProvider *provider, GError **error); gboolean fu_provider_update (FuProvider *provider, FuDevice *device, - GInputStream *stream_cab, - gint fd_fw, + GBytes *blob_cab, + GBytes *blob_fw, FuProviderFlags flags, GError **error); gboolean fu_provider_verify (FuProvider *provider, diff --git a/src/fu-self-test.c b/src/fu-self-test.c index 55ac71c47..17d3efded 100644 --- a/src/fu-self-test.c +++ b/src/fu-self-test.c @@ -27,7 +27,6 @@ #include #include -#include "fu-cab.h" #include "fu-keyring.h" #include "fu-pending.h" #include "fu-provider-fake.h" @@ -162,64 +161,6 @@ fu_rom_all_func (void) } while (TRUE); } -static void -fu_cab_func (void) -{ - GError *error = NULL; - gboolean ret; - g_autofree gchar *filename = NULL; - g_autoptr(FuCab) cab = NULL; - g_autoptr(GFile) file = NULL; - - cab = fu_cab_new (); - g_assert (cab != NULL); - - /* load file */ - filename = fu_test_get_filename ("colorhug/colorhug-als-3.0.2.cab"); - g_assert (filename != NULL); - file = g_file_new_for_path (filename); - ret = fu_cab_load_file (cab, file, NULL, &error); - g_assert_no_error (error); - g_assert (ret); - - /* get properties */ - g_assert (fu_cab_get_stream (cab) != NULL); - g_assert_cmpstr (fu_cab_get_guid (cab), ==, "84f40464-9272-4ef7-9399-cd95f12da696"); - g_assert_cmpstr (fu_cab_get_version (cab), ==, "3.0.2"); - g_assert_cmpstr (fu_cab_get_url_homepage (cab), ==, "http://www.hughski.com/"); - g_assert_cmpstr (fu_cab_get_license (cab), ==, "GPL-2.0+"); - g_assert_cmpint (fu_cab_get_size (cab), ==, 10325); - g_assert_cmpstr (fu_cab_get_description (cab), !=, NULL); - g_assert_cmpint (fu_cab_get_trust_flags (cab), ==, FWUPD_TRUST_FLAG_NONE); - g_assert (!g_file_test (fu_cab_get_filename_firmware (cab), G_FILE_TEST_EXISTS)); - - /* extract firmware */ - ret = fu_cab_extract (cab, FU_CAB_EXTRACT_FLAG_FIRMWARE | - FU_CAB_EXTRACT_FLAG_SIGNATURE, &error); - g_assert_no_error (error); - g_assert (ret); - g_assert (g_str_has_suffix (fu_cab_get_filename_firmware (cab), "/firmware.bin")); - g_assert (g_file_test (fu_cab_get_filename_firmware (cab), G_FILE_TEST_EXISTS)); - - /* this is not available in make distcheck */ - ret = fu_cab_verify (cab, &error); - if (g_error_matches (error, FWUPD_ERROR, FWUPD_ERROR_NOT_FOUND)) { - g_clear_error (&error); - } else { - g_assert_no_error (error); - g_assert (ret); - - /* check the certificate */ - g_assert_cmpint (fu_cab_get_trust_flags (cab), ==, FWUPD_TRUST_FLAG_PAYLOAD); - } - - /* clean up */ - ret = fu_cab_delete_temp_files (cab, &error); - g_assert_no_error (error); - g_assert (ret); - g_assert (!g_file_test (fu_cab_get_filename_firmware (cab), G_FILE_TEST_EXISTS)); -} - static void _provider_status_changed_cb (FuProvider *provider, FwupdStatus status, gpointer user_data) { @@ -538,7 +479,6 @@ main (int argc, char **argv) /* tests go here */ g_test_add_func ("/fwupd/rom", fu_rom_func); g_test_add_func ("/fwupd/rom{all}", fu_rom_all_func); - g_test_add_func ("/fwupd/cab", fu_cab_func); g_test_add_func ("/fwupd/pending", fu_pending_func); g_test_add_func ("/fwupd/provider", fu_provider_func); g_test_add_func ("/fwupd/provider{rpi}", fu_provider_rpi_func);