From deea2da041ab728d89a3ad59fc1dbc1ef110b84d Mon Sep 17 00:00:00 2001 From: Richard Hughes Date: Fri, 15 Dec 2017 15:40:44 +0000 Subject: [PATCH] Use the new functionality in libgcab >= 1.0 to avoid writing temp files Using old versions of gcab we could only do one thing: extract the files in the cabinet archive to a new directory in /tmp, and then fwupd would have to read them back in to memory to parse them. This was both inelegant and wasteful, and probably not an awesome idea from a security or privacy point of view. Using libgcab >= 1.0 we can decompress to a GBytes blob, and then verify the firmware and metainfo file without anything being written to disk. As this is a security sensitive operation, move the fwupd-specific helper code out of libappstream-glib and also add a lot of internal self tests. The gcab code will have to remain in libappstream-glib for a long time, but we don't have to use it. Handling the cab file here also allows us to fix two long-standing bugs: * MetaInfo or firmware files in a subdirectory are handled correctly * The archive can also be self-signed using PKCS7 instead of using GPG --- meson.build | 7 +- src/fu-common-cab.c | 327 ++++++++++++++++++++++++++++++++++++++++++++ src/fu-common-cab.h | 30 ++++ src/fu-engine.c | 15 +- src/fu-self-test.c | 320 +++++++++++++++++++++++++++++++++++++++++++ src/meson.build | 4 + 6 files changed, 690 insertions(+), 13 deletions(-) create mode 100644 src/fu-common-cab.c create mode 100644 src/fu-common-cab.h diff --git a/meson.build b/meson.build index 77ac42510..efe73862f 100644 --- a/meson.build +++ b/meson.build @@ -163,10 +163,13 @@ endif libm = cc.find_library('m', required: false) udev = dependency('udev') uuid = dependency('uuid') -gcab = dependency('libgcab-1.0') -if gcab.version().version_compare('>= 0.8') +libgcab = dependency('libgcab-1.0') +if libgcab.version().version_compare('>= 0.8') conf.set('HAVE_GCAB_0_8', '1') endif +if libgcab.version().version_compare('>= 1.0') + conf.set('HAVE_GCAB_1_0', '1') +endif if get_option('plugin_uefi_labels') cairo = dependency('cairo') diff --git a/src/fu-common-cab.c b/src/fu-common-cab.c new file mode 100644 index 000000000..ef8c7f2a2 --- /dev/null +++ b/src/fu-common-cab.c @@ -0,0 +1,327 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- + * + * Copyright (C) 2017 Richard Hughes + * + * Licensed under the GNU Lesser General Public License Version 2.1 + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "config.h" + +#include + +#include "fu-common-cab.h" +#include "fu-common.h" + +#include "fwupd-error.h" + +#ifdef HAVE_GCAB_1_0 + +/* sets the firmware and signature blobs on AsRelease */ +static gboolean +fu_common_store_from_cab_release (AsRelease *release, GCabFolder *cabfolder, GError **error) +{ + AsChecksum *csum_tmp; + GCabFile *cabfile; + GBytes *blob; + guint64 size; + g_autofree gchar *basename = NULL; + g_autofree gchar *checksum = NULL; + const gchar *suffixes[] = { "asc", "p7b", "p7c", NULL }; + + /* ensure we always have a content checksum */ + csum_tmp = as_release_get_checksum_by_target (release, AS_CHECKSUM_TARGET_CONTENT); + if (csum_tmp == NULL) { + g_autoptr(AsChecksum) csum = as_checksum_new (); + as_checksum_set_target (csum, AS_CHECKSUM_TARGET_CONTENT); + /* if this isn't true, a firmware needs to set in + * the metainfo.xml file something like: + * */ + as_checksum_set_filename (csum, "firmware.bin"); + as_release_add_checksum (release, csum); + csum_tmp = csum; + } + + /* get the main firmware file */ + basename = g_path_get_basename (as_checksum_get_filename (csum_tmp)); + cabfile = gcab_folder_get_file_by_name (cabfolder, basename); + if (cabfile == NULL) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "cannot find %s in archive", + as_checksum_get_filename (csum_tmp)); + return FALSE; + } + blob = gcab_file_get_bytes (cabfile); + if (blob == NULL) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "no GBytes from GCabFile firmware"); + return FALSE; + } + + /* set the blob */ + as_release_set_blob (release, basename, blob); + + /* set if unspecified, but error out if specified and incorrect */ + size = as_release_get_size (release, AS_SIZE_KIND_INSTALLED); + if (size == 0) { + as_release_set_size (release, AS_SIZE_KIND_INSTALLED, + g_bytes_get_size (blob)); + } else if (size != g_bytes_get_size (blob)) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "contents size invalid, expected " + "%" G_GUINT64_FORMAT ", got %" G_GUINT64_FORMAT, + g_bytes_get_size (blob), size); + return FALSE; + } + + /* set if unspecified, but error out if specified and incorrect */ + checksum = g_compute_checksum_for_bytes (G_CHECKSUM_SHA1, blob); + if (as_checksum_get_value (csum_tmp) == NULL) { + as_checksum_set_value (csum_tmp, checksum); + } else if (g_strcmp0 (checksum, as_checksum_get_value (csum_tmp)) != 0) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "contents checksum invalid, expected %s, got %s", + checksum, + as_checksum_get_value (csum_tmp)); + return FALSE; + } + + /* if the signing file exists, set that too */ + for (guint i = 0; suffixes[i] != NULL; i++) { + g_autofree gchar *basename_sig = NULL; + basename_sig = g_strdup_printf ("%s.%s", basename, suffixes[i]); + cabfile = gcab_folder_get_file_by_name (cabfolder, basename_sig); + if (cabfile != NULL) { + blob = gcab_file_get_bytes (cabfile); + if (blob == NULL) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "no GBytes from GCabFile %s", + basename_sig); + return FALSE; + } + as_release_set_blob (release, basename_sig, blob); + } + } + + /* success */ + return TRUE; +} + +/* adds each GCabFile to the store */ +static gboolean +fu_common_store_from_cab_file (AsStore *store, GCabFolder *cabfolder, + GCabFile *cabfile, GError **error) +{ + GBytes *blob; + GPtrArray *releases; + g_autoptr(AsApp) app = NULL; + g_autoptr(GError) error_local = NULL; +#if !AS_CHECK_VERSION(0,7,5) + g_autofree gchar *cache_fn = NULL; +#endif + + /* parse file */ + blob = gcab_file_get_bytes (cabfile); + if (blob == NULL) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "no GBytes from GCabFile"); + return FALSE; + } + app = as_app_new (); +#if AS_CHECK_VERSION(0,7,5) + if (!as_app_parse_data (app, blob, AS_APP_PARSE_FLAG_NONE, &error_local)) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "counld not parse MetaInfo XML: %s", + error_local->message); + return FALSE; + } +#else + cache_fn = g_build_filename (LOCALSTATEDIR, "cache", "fwupd", + gcab_file_get_extract_name (cabfile), NULL); + if (!fu_common_mkdir_parent (cache_fn, error)) + return FALSE; + if (!g_file_set_contents (cache_fn, g_bytes_get_data (blob, NULL), + g_bytes_get_size (blob), &error_local)) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_WRITE, + "counld not save temporary MetaInfo XML to %s: %s", + cache_fn, error_local->message); + return FALSE; + } + if (!as_app_parse_file (app, cache_fn, AS_APP_PARSE_FLAG_NONE, &error_local)) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "counld not parse MetaInfo XML: %s", + error_local->message); + return FALSE; + } +#endif + + /* process each listed release */ + releases = as_app_get_releases (app); + if (releases->len == 0) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "no releases in metainfo file"); + return FALSE; + } + for (guint i = 0; i < releases->len; i++) { + AsRelease *release = g_ptr_array_index (releases, i); + g_debug ("processing release: %s", as_release_get_version (release)); + if (!fu_common_store_from_cab_release (release, cabfolder, error)) + return FALSE; + } + + /* success */ + as_store_add_app (store, app); + return TRUE; +} + +/* adds each GCabFolder to the store */ +static gboolean +fu_common_store_from_cab_folder (AsStore *store, GCabFolder *cabfolder, GError **error) +{ + g_autoptr(GSList) cabfiles = gcab_folder_get_files (cabfolder); + for (GSList *l = cabfiles; l != NULL; l = l->next) { + GCabFile *cabfile = GCAB_FILE (l->data); + const gchar *fn = gcab_file_get_extract_name (cabfile); + g_debug ("processing file: %s", fn); + if (as_format_guess_kind (fn) == AS_FORMAT_KIND_METAINFO) { + if (!fu_common_store_from_cab_file (store, cabfolder, cabfile, error)) { + g_prefix_error (error, "%s could not be loaded: ", + gcab_file_get_extract_name (cabfile)); + return FALSE; + } + } + } + return TRUE; +} + +static gboolean +as_cab_store_file_cb (GCabFile *file, gpointer user_data) +{ + g_autofree gchar *basename = NULL; + g_autofree gchar *name = NULL; + + /* convert to UNIX paths */ + name = g_strdup (gcab_file_get_name (file)); + g_strdelimit (name, "\\", '/'); + + /* ignore the dirname completely */ + basename = g_path_get_basename (name); + gcab_file_set_extract_name (file, basename); + return TRUE; +} + +/** + * fu_common_store_from_cab_bytes: + * @blob: A readable blob + * @error: A #FuEndianType, e.g. %G_LITTLE_ENDIAN + * + * Create an AppStream store from a cabinet archive. + * + * Returns: a store, or %NULL on error + **/ +AsStore * +fu_common_store_from_cab_bytes (GBytes *blob, GError **error) +{ + GPtrArray *folders; + g_autoptr(AsStore) store = as_store_new (); + g_autoptr(GCabCabinet) cabinet = gcab_cabinet_new (); + g_autoptr(GError) error_local = NULL; + g_autoptr(GInputStream) ip = NULL; + + /* decompress the file to memory */ + ip = g_memory_input_stream_new_from_bytes (blob); + if (!gcab_cabinet_load (cabinet, ip, NULL, error)) + return FALSE; + if (!gcab_cabinet_extract_simple (cabinet, NULL, + as_cab_store_file_cb, NULL, + NULL, &error_local)) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + error_local->message); + return NULL; + } + + /* look at each folder */ + folders = gcab_cabinet_get_folders (cabinet); + for (guint i = 0; i < folders->len; i++) { + GCabFolder *cabfolder = GCAB_FOLDER (g_ptr_array_index (folders, i)); + g_debug ("processing folder: %u/%u", i + 1, folders->len); + if (!fu_common_store_from_cab_folder (store, cabfolder, error)) + return NULL; + } + + /* did we get any valid AsApps */ + if (as_store_get_size (store) == 0) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "archive contained no valid metadata"); + return FALSE; + } + + /* success */ + return g_steal_pointer (&store); +} + +#else + +AsStore * +fu_common_store_from_cab_bytes (GBytes *blob, GError **error) +{ + g_autoptr(AsStore) store = as_store_new (); + g_autoptr(GError) error_local = NULL; + + /* this is klunky as we have to write actual files to /tmp */ + if (!as_store_from_bytes (store, blob, NULL, &error_local)) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + error_local->message); + return NULL; + } + + /* did we get any valid AsApps */ + if (as_store_get_size (store) == 0) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "archive contained no valid metadata"); + return FALSE; + } + return g_steal_pointer (&store); +} +#endif diff --git a/src/fu-common-cab.h b/src/fu-common-cab.h new file mode 100644 index 000000000..b51970d2e --- /dev/null +++ b/src/fu-common-cab.h @@ -0,0 +1,30 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- + * + * Copyright (C) 2017 Richard Hughes + * + * Licensed under the GNU Lesser General Public License Version 2.1 + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef __FU_COMMON_CAB_H +#define __FU_COMMON_CAB_H + +#include + +AsStore *fu_common_store_from_cab_bytes (GBytes *blob, + GError **error); + +#endif /* __FU_COMMON_CAB_H */ diff --git a/src/fu-engine.c b/src/fu-engine.c index 75923d711..8309f7584 100644 --- a/src/fu-engine.c +++ b/src/fu-engine.c @@ -34,6 +34,7 @@ #include "fwupd-remote-private.h" #include "fwupd-resources.h" +#include "fu-common-cab.h" #include "fu-common.h" #include "fu-config.h" #include "fu-debug.h" @@ -1973,27 +1974,19 @@ fu_engine_get_store_from_blob (FuEngine *self, GBytes *blob_cab, GError **error) { g_autofree gchar *checksum = NULL; g_autoptr(AsStore) store = NULL; - g_autoptr(GError) error_local = NULL; g_return_val_if_fail (FU_IS_ENGINE (self), NULL); g_return_val_if_fail (blob_cab != NULL, NULL); g_return_val_if_fail (error == NULL || *error == NULL, NULL); /* load file */ - store = as_store_new (); fu_engine_set_status (self, FWUPD_STATUS_DECOMPRESSING); - if (!as_store_from_bytes (store, blob_cab, NULL, &error_local)) { - g_set_error_literal (error, - FWUPD_ERROR, - FWUPD_ERROR_INVALID_FILE, - error_local->message); + store = fu_common_store_from_cab_bytes (blob_cab, error); + if (store == NULL) return NULL; - } /* get a checksum of the file and use it as the origin */ - checksum = g_compute_checksum_for_data (G_CHECKSUM_SHA256, - g_bytes_get_data (blob_cab, NULL), - g_bytes_get_size (blob_cab)); + checksum = g_compute_checksum_for_bytes (G_CHECKSUM_SHA256, blob_cab); as_store_set_origin (store, checksum); fu_engine_set_status (self, FWUPD_STATUS_IDLE); diff --git a/src/fu-self-test.c b/src/fu-self-test.c index c7a44adc4..e16aabc42 100644 --- a/src/fu-self-test.c +++ b/src/fu-self-test.c @@ -26,9 +26,11 @@ #include #include #include +#include #include #include +#include "fu-common-cab.h" #include "fu-config.h" #include "fu-device-list.h" #include "fu-device-private.h" @@ -1358,6 +1360,317 @@ fu_common_endian_func (void) g_assert_cmpint (fu_common_read_uint16 (buf, G_BIG_ENDIAN), ==, 0x1234); } +static GBytes * +_build_cab (GCabCompression compression, ...) +{ +#ifdef HAVE_GCAB_1_0 + gboolean ret; + va_list args; + g_autoptr(GCabCabinet) cabinet = NULL; + g_autoptr(GCabFolder) cabfolder = NULL; + g_autoptr(GError) error = NULL; + g_autoptr(GOutputStream) op = NULL; + + /* create a new archive */ + cabinet = gcab_cabinet_new (); + cabfolder = gcab_folder_new (compression); + ret = gcab_cabinet_add_folder (cabinet, cabfolder, &error); + g_assert_no_error (error); + g_assert (ret); + + /* add each file */ + va_start (args, compression); + do { + const gchar *fn; + const gchar *text; + g_autoptr(GCabFile) cabfile = NULL; + g_autoptr(GBytes) blob = NULL; + + /* get filename */ + fn = va_arg (args, const gchar *); + if (fn == NULL) + break; + + /* get contents */ + text = va_arg (args, const gchar *); + if (text == NULL) + break; + g_debug ("creating %s with %s", fn, text); + + /* add a GCabFile to the cabinet */ + blob = g_bytes_new_static (text, strlen (text)); + cabfile = gcab_file_new_with_bytes (fn, blob); + ret = gcab_folder_add_file (cabfolder, cabfile, FALSE, NULL, &error); + g_assert_no_error (error); + g_assert (ret); + } while (TRUE); + va_end (args); + + /* write the archive to a blob */ + op = g_memory_output_stream_new_resizable (); + ret = gcab_cabinet_write_simple (cabinet, op, NULL, NULL, NULL, &error); + g_assert_no_error (error); + g_assert (ret); + ret = g_output_stream_close (op, NULL, &error); + g_assert_no_error (error); + g_assert (ret); + return g_memory_output_stream_steal_as_bytes (G_MEMORY_OUTPUT_STREAM (op)); +#else + return NULL; +#endif +} + +static void +fu_common_store_cab_func (void) +{ + AsApp *app; + AsChecksum *csum; + AsRelease *rel; + AsRequire *req; + GBytes *blob_tmp; + g_autoptr(AsStore) store = NULL; + g_autoptr(GBytes) blob = NULL; + g_autoptr(GError) error = NULL; + + /* create store */ + blob = _build_cab (GCAB_COMPRESSION_NONE, + "acme.metainfo.xml", + "\n" + " com.acme.example.firmware\n" + " ACME Firmware\n" + " \n" + " ae56e3fb-6528-5bc4-8b03-012f124075d7\n" + " \n" + " \n" + " \n" + " \n" + " 5\n" + " 7c211433f02071597741e6ff5a8ea34789abbf43\n" + "

We fixed things

\n" + "
\n" + "
\n" + " \n" + " org.freedesktop.fwupd\n" + " \n" + "
", + "firmware.dfu", "world", + "firmware.dfu.asc", "signature", + NULL); + if (blob == NULL) { + g_test_skip ("libgcab too old"); + return; + } + store = fu_common_store_from_cab_bytes (blob, &error); + g_assert_no_error (error); + g_assert (store != NULL); + + /* verify */ + app = as_store_get_app_by_id (store, "com.acme.example.firmware"); + g_assert_nonnull (app); + rel = as_app_get_release_default (app); + g_assert_nonnull (rel); + g_assert_cmpstr (as_release_get_version (rel), ==, "1.2.3"); + csum = as_release_get_checksum_by_target (rel, AS_CHECKSUM_TARGET_CONTENT); + g_assert_cmpstr (as_checksum_get_value (csum), ==, "7c211433f02071597741e6ff5a8ea34789abbf43"); + blob_tmp = as_release_get_blob (rel, "firmware.dfu"); + g_assert_nonnull (blob_tmp); + blob_tmp = as_release_get_blob (rel, "firmware.dfu.asc"); + g_assert_nonnull (blob_tmp); + req = as_app_get_require_by_value (app, AS_REQUIRE_KIND_ID, "org.freedesktop.fwupd"); + g_assert_nonnull (req); +} + +static void +fu_common_store_cab_unsigned_func (void) +{ + AsApp *app; + AsChecksum *csum; + AsRelease *rel; + GBytes *blob_tmp; + g_autoptr(AsStore) store = NULL; + g_autoptr(GBytes) blob = NULL; + g_autoptr(GError) error = NULL; + + /* create store */ + blob = _build_cab (GCAB_COMPRESSION_NONE, + "acme.metainfo.xml", + "\n" + " com.acme.example.firmware\n" + " \n" + " \n" + " \n" + "", + "firmware.bin", "world", + NULL); + if (blob == NULL) { + g_test_skip ("libgcab too old"); + return; + } + store = fu_common_store_from_cab_bytes (blob, &error); + g_assert_no_error (error); + g_assert (store != NULL); + + /* verify */ + app = as_store_get_app_by_id (store, "com.acme.example.firmware"); + g_assert_nonnull (app); + rel = as_app_get_release_default (app); + g_assert_nonnull (rel); + g_assert_cmpstr (as_release_get_version (rel), ==, "1.2.3"); + csum = as_release_get_checksum_by_target (rel, AS_CHECKSUM_TARGET_CONTENT); + g_assert_cmpstr (as_checksum_get_value (csum), ==, "7c211433f02071597741e6ff5a8ea34789abbf43"); + blob_tmp = as_release_get_blob (rel, "firmware.bin"); + g_assert_nonnull (blob_tmp); + blob_tmp = as_release_get_blob (rel, "firmware.bin.asc"); + g_assert_null (blob_tmp); +} + +static void +fu_common_store_cab_folder_func (void) +{ + AsApp *app; + AsChecksum *csum; + AsRelease *rel; + GBytes *blob_tmp; + g_autoptr(AsStore) store = NULL; + g_autoptr(GBytes) blob = NULL; + g_autoptr(GError) error = NULL; + + /* create store */ + blob = _build_cab (GCAB_COMPRESSION_NONE, + "lvfs\\acme.metainfo.xml", + "\n" + " com.acme.example.firmware\n" + " \n" + " \n" + " \n" + "", + "lvfs\\firmware.bin", "world", + NULL); + if (blob == NULL) { + g_test_skip ("libgcab too old"); + return; + } + store = fu_common_store_from_cab_bytes (blob, &error); + g_assert_no_error (error); + g_assert (store != NULL); + + /* verify */ + app = as_store_get_app_by_id (store, "com.acme.example.firmware"); + g_assert_nonnull (app); + rel = as_app_get_release_default (app); + g_assert_nonnull (rel); + g_assert_cmpstr (as_release_get_version (rel), ==, "1.2.3"); + csum = as_release_get_checksum_by_target (rel, AS_CHECKSUM_TARGET_CONTENT); + g_assert_cmpstr (as_checksum_get_value (csum), ==, "7c211433f02071597741e6ff5a8ea34789abbf43"); + blob_tmp = as_release_get_blob (rel, "firmware.bin"); + g_assert_nonnull (blob_tmp); +} + +static void +fu_common_store_cab_error_no_metadata_func (void) +{ + g_autoptr(AsStore) store = NULL; + g_autoptr(GBytes) blob = NULL; + g_autoptr(GError) error = NULL; + + blob = _build_cab (GCAB_COMPRESSION_NONE, + "foo.txt", "hello", + "bar.txt", "world", + NULL); + if (blob == NULL) { + g_test_skip ("libgcab too old"); + return; + } + store = fu_common_store_from_cab_bytes (blob, &error); + g_assert_error (error, FWUPD_ERROR, FWUPD_ERROR_INVALID_FILE); + g_assert (store == NULL); +} + +static void +fu_common_store_cab_error_wrong_size_func (void) +{ + g_autoptr(AsStore) store = NULL; + g_autoptr(GBytes) blob = NULL; + g_autoptr(GError) error = NULL; + + blob = _build_cab (GCAB_COMPRESSION_NONE, + "acme.metainfo.xml", + "\n" + " com.acme.example.firmware\n" + " \n" + " \n" + " 7004701\n" + " deadbeef\n" + " \n" + " \n" + "", + "firmware.bin", "world", + NULL); + if (blob == NULL) { + g_test_skip ("libgcab too old"); + return; + } + store = fu_common_store_from_cab_bytes (blob, &error); + g_assert_error (error, FWUPD_ERROR, FWUPD_ERROR_INVALID_FILE); + g_assert (store == NULL); +} + +static void +fu_common_store_cab_error_missing_file_func (void) +{ + g_autoptr(AsStore) store = NULL; + g_autoptr(GBytes) blob = NULL; + g_autoptr(GError) error = NULL; + + blob = _build_cab (GCAB_COMPRESSION_NONE, + "acme.metainfo.xml", + "\n" + " com.acme.example.firmware\n" + " \n" + " \n" + " \n" + " \n" + " \n" + "", + "firmware.bin", "world", + NULL); + if (blob == NULL) { + g_test_skip ("libgcab too old"); + return; + } + store = fu_common_store_from_cab_bytes (blob, &error); + g_assert_error (error, FWUPD_ERROR, FWUPD_ERROR_INVALID_FILE); + g_assert (store == NULL); +} + +static void +fu_common_store_cab_error_wrong_checksum_func (void) +{ + g_autoptr(AsStore) store = NULL; + g_autoptr(GBytes) blob = NULL; + g_autoptr(GError) error = NULL; + + blob = _build_cab (GCAB_COMPRESSION_NONE, + "acme.metainfo.xml", + "\n" + " com.acme.example.firmware\n" + " \n" + " \n" + " deadbeef\n" + " \n" + " \n" + "", + "firmware.bin", "world", + NULL); + if (blob == NULL) { + g_test_skip ("libgcab too old"); + return; + } + store = fu_common_store_from_cab_bytes (blob, &error); + g_assert_error (error, FWUPD_ERROR, FWUPD_ERROR_INVALID_FILE); + g_assert (store == NULL); +} + int main (int argc, char **argv) { @@ -1393,6 +1706,13 @@ main (int argc, char **argv) g_test_add_func ("/fwupd/keyring{gpg}", fu_keyring_gpg_func); g_test_add_func ("/fwupd/keyring{pkcs7}", fu_keyring_pkcs7_func); g_test_add_func ("/fwupd/common{endian}", fu_common_endian_func); + g_test_add_func ("/fwupd/common{cab-success}", fu_common_store_cab_func); + g_test_add_func ("/fwupd/common{cab-success-unsigned}", fu_common_store_cab_unsigned_func); + g_test_add_func ("/fwupd/common{cab-success-folder}", fu_common_store_cab_folder_func); + g_test_add_func ("/fwupd/common{cab-error-no-metadata}", fu_common_store_cab_error_no_metadata_func); + g_test_add_func ("/fwupd/common{cab-error-wrong-size}", fu_common_store_cab_error_wrong_size_func); + g_test_add_func ("/fwupd/common{cab-error-wrong-checksum}", fu_common_store_cab_error_wrong_checksum_func); + g_test_add_func ("/fwupd/common{cab-error-missing-file}", fu_common_store_cab_error_missing_file_func); g_test_add_func ("/fwupd/common{spawn)", fu_common_spawn_func); g_test_add_func ("/fwupd/common{firmware-builder}", fu_common_firmware_builder_func); return g_test_run (); diff --git a/src/meson.build b/src/meson.build index 5e02f38a3..8ce2509e7 100644 --- a/src/meson.build +++ b/src/meson.build @@ -120,6 +120,7 @@ executable( sources : [ keyring_src, 'fu-common.c', + 'fu-common-cab.c', 'fu-config.c', 'fu-keyring.c', 'fu-keyring-result.c', @@ -145,6 +146,7 @@ executable( dependencies : [ keyring_deps, appstream_glib, + libgcab, giounix, gmodule, gudev, @@ -185,6 +187,7 @@ if get_option('tests') keyring_src, 'fu-self-test.c', 'fu-common.c', + 'fu-common-cab.c', 'fu-config.c', 'fu-engine.c', 'fu-keyring.c', @@ -210,6 +213,7 @@ if get_option('tests') dependencies : [ keyring_deps, appstream_glib, + libgcab, giounix, gmodule, gudev,