From 283958ee4407ff8681728033790c685e2b8cf574 Mon Sep 17 00:00:00 2001 From: peng2285 Date: Fri, 15 Jul 2022 15:25:16 +0800 Subject: [PATCH] CVE-2020-10759 --- ...d12fccef63b8aaa99ec53278ce18250b0444.patch | 37 + ...89034c3d34ae4ac4530ea7b6b16e82476fae.patch | 841 ++++++++++++++++++ ...89f45a38b2373bf5836337a33f450aaab72e.patch | 111 +++ libxmlb.spec | 17 +- 4 files changed, 1005 insertions(+), 1 deletion(-) create mode 100644 21f2d12fccef63b8aaa99ec53278ce18250b0444.patch create mode 100644 36a889034c3d34ae4ac4530ea7b6b16e82476fae.patch create mode 100644 839b89f45a38b2373bf5836337a33f450aaab72e.patch diff --git a/21f2d12fccef63b8aaa99ec53278ce18250b0444.patch b/21f2d12fccef63b8aaa99ec53278ce18250b0444.patch new file mode 100644 index 0000000..88fb1a7 --- /dev/null +++ b/21f2d12fccef63b8aaa99ec53278ce18250b0444.patch @@ -0,0 +1,37 @@ +From 21f2d12fccef63b8aaa99ec53278ce18250b0444 Mon Sep 17 00:00:00 2001 +From: Richard Hughes +Date: Thu, 28 May 2020 16:42:18 +0100 +Subject: [PATCH] Validate that gpgme_op_verify_result() returned at least one + signature + +If a detached signature is actually a PGP message, gpgme_op_verify() returns +the rather perplexing GPG_ERR_NO_ERROR, and then gpgme_op_verify_result() +builds an empty list. + +Explicitly check for no signatures present to avoid returning a FuKeyringResult +with no timestamp and an empty authority. + +Many thanks to Justin Steven for the discovery and +coordinated disclosure of this issue. Fixes CVE-2020-10759 +--- + src/fu-keyring-gpg.c | 7 +++++++ + 1 file changed, 7 insertions(+) + +diff --git a/src/fu-keyring-gpg.c b/src/fu-keyring-gpg.c +index f06449cf2d..15d6e2c320 100644 +--- a/src/fu-keyring-gpg.c ++++ b/src/fu-keyring-gpg.c +@@ -297,6 +297,13 @@ fu_keyring_gpg_verify_data (FuKeyring *keyring, + "no result record from libgpgme"); + return NULL; + } ++ if (result->signatures == NULL) { ++ g_set_error_literal (error, ++ FWUPD_ERROR, ++ FWUPD_ERROR_INTERNAL, ++ "no signatures from libgpgme"); ++ return NULL; ++ } + + /* look at each signature */ + for (s = result->signatures; s != NULL ; s = s->next ) { diff --git a/36a889034c3d34ae4ac4530ea7b6b16e82476fae.patch b/36a889034c3d34ae4ac4530ea7b6b16e82476fae.patch new file mode 100644 index 0000000..bb75349 --- /dev/null +++ b/36a889034c3d34ae4ac4530ea7b6b16e82476fae.patch @@ -0,0 +1,841 @@ +From 36a889034c3d34ae4ac4530ea7b6b16e82476fae Mon Sep 17 00:00:00 2001 +From: Richard Hughes +Date: Tue, 14 Apr 2015 13:52:34 +0100 +Subject: [PATCH] Add helper code to validate public key signatures + +We'll use this in the future for checking device firmware. +--- + configure.ac | 15 + + contrib/fwupd.spec.in | 1 + + data/Makefile.am | 2 +- + data/pki/GPG-KEY-Hughski-Limited | 30 ++ + data/pki/Makefile.am | 4 + + data/tests/Makefile.am | 5 +- + data/tests/firmware.bin | Bin 0 -> 8000 bytes + data/tests/pki/GPG-KEY-Hughski-Limited | 1 + + libfwupd/fwupd-error.c | 3 + + libfwupd/fwupd-error.h | 2 + + src/Makefile.am | 7 + + src/fu-keyring.c | 368 +++++++++++++++++++++++++ + src/fu-keyring.h | 70 +++++ + src/fu-self-test.c | 41 +++ + 14 files changed, 547 insertions(+), 2 deletions(-) + create mode 100644 data/pki/GPG-KEY-Hughski-Limited + create mode 100644 data/pki/Makefile.am + create mode 100644 data/tests/firmware.bin + create mode 120000 data/tests/pki/GPG-KEY-Hughski-Limited + create mode 100644 src/fu-keyring.c + create mode 100644 src/fu-keyring.h + +diff --git a/configure.ac b/configure.ac +index 894fd05d0b..4ea8fd178a 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -132,6 +132,20 @@ if test x$enable_colorhug != xno; then + fi + AM_CONDITIONAL(HAVE_COLORHUG, test x$enable_colorhug = xyes) + ++# gpgme support ++AC_MSG_CHECKING([for gpgme]) ++if ! test -x "/usr/bin/gpgme-config"; then ++ AC_MSG_ERROR([Cannot locate gpgme]) ++else ++ AC_MSG_RESULT([yes]) ++ GPGME_CFLAGS="`\"/usr/bin/gpgme-config\" --cflags`" ++ GPGME_LIBS="`\"/usr/bin/gpgme-config\" --libs`" ++ GPGME_CFLAGS+=" `\"/usr/bin/gpg-error-config\" --cflags`" ++ GPGME_LIBS+=" `\"/usr/bin/gpg-error-config\" --libs`" ++ AC_SUBST([GPGME_CFLAGS]) ++ AC_SUBST([GPGME_LIBS]) ++fi ++ + # UEFI support + AC_ARG_ENABLE(uefi, AS_HELP_STRING([--enable-uefi],[Enable ColorHug support]), + enable_uefi=$enableval, enable_uefi=yes) +@@ -161,6 +175,7 @@ libfwupd/fwupd.pc + libfwupd/Makefile + man/Makefile + data/Makefile ++data/pki/Makefile + data/tests/Makefile + policy/Makefile + po/Makefile.in +diff --git a/contrib/fwupd.spec.in b/contrib/fwupd.spec.in +index fc71a6bb6d..1c0b0cb6b8 100644 +--- a/contrib/fwupd.spec.in ++++ b/contrib/fwupd.spec.in +@@ -76,6 +76,7 @@ find %{buildroot} -name '*.la' -exec rm -f {} ';' + %license COPYING + %{_libexecdir}/fwupd + %{_bindir}/fwupdmgr ++%{_sysconfdir}/pki/fwupd + %{_sysconfdir}/dbus-1/system.d/org.freedesktop.fwupd.conf + %{_datadir}/dbus-1/interfaces/org.freedesktop.fwupd.xml + %{_datadir}/polkit-1/actions/org.freedesktop.fwupd.policy +diff --git a/data/Makefile.am b/data/Makefile.am +index 8e364d4add..5568208aee 100644 +--- a/data/Makefile.am ++++ b/data/Makefile.am +@@ -1,4 +1,4 @@ +-SUBDIRS = tests ++SUBDIRS = tests pki + + dbusdir = $(sysconfdir)/dbus-1/system.d + dist_dbus_DATA = org.freedesktop.fwupd.conf +diff --git a/data/pki/GPG-KEY-Hughski-Limited b/data/pki/GPG-KEY-Hughski-Limited +new file mode 100644 +index 0000000000..10fb387739 +--- /dev/null ++++ b/data/pki/GPG-KEY-Hughski-Limited +@@ -0,0 +1,30 @@ ++-----BEGIN PGP PUBLIC KEY BLOCK----- ++Version: GnuPG v1 ++ ++mQENBFUr0UoBCACsdOLuTJ81dICrSvUhyznBsL4WgEa2RUbEjJuaXwrEyPMikHE1 ++Clda2YI7VbpCgIVq8Zy63CGJ4Xqs2T6pyetaXnbX8J0C+7wg2IfPv7pUyCsP7/JR ++HRB2GNelCWrsGArN1cOPI0ESH4yHWKF9KCGlpsLfSHmvF7D8vcKlKQUlO4T6lxOP ++SNjMSXkMsxfDDhl1mzqrwxfU4V6nnPcuMwU7tvg+39PioP4Ny1tKP4SSpBfh7qwz ++XXRd505dqNLOubxmOPZ5rznVkKmW2cwahO6fr5zVA8/2TDZQ79mdbfvSJVlW06qs ++C5PYmLnBjyzE5uQ4oxSIuUEiMfqrn3Qs6PhhABEBAAG0Ikh1Z2hza2kgTGltaXRl ++ZCA8aW5mb0BodWdoc2tpLmNvbT6JATgEEwECACIFAlUr0UoCGwMGCwkIBwMCBhUI ++AgkKCwQWAgMBAh4BAheAAAoJEK2KUo/sRIge/fUH/Rblgzh5GeB0Zp2U9W+r26iJ ++t1AD5a/fKxQahz/pwMkevQCCMzI1vpX12P3HtACZOD3Zjh9RXY6Z3033YZjrRApe ++FkOVfcyUF1nP/z2Ox3jE3+B8v1u0UzH/MqtF/1095mqvR7gllE288KDqu7bvd5l3 ++z4IETk5qqoeCe9LYc8aob973dbocyS/gou/FLCKxoXVEe8DPRwv8qmXlXOujxdxd ++FcslpYqtjj4fgUswQ/cY/a1UcAX5zCnVqFbU7oJH2uTNewKuaZ2wgPbnzvwx8JYl ++VfFdPN7GZ0NMrZDLeJ0SLXer/9+qAKNH4UpQS9axXQL+VKOzsZCXuv31VDCj5Jy5 ++AQ0EVSvRSgEIAMgVrZP3LmA9bx7B8l+agVh5DNXrMixX9jhZ0Yfn8+UIMMNTZziD ++ZV3nXxswKPrcsqQ+KP9iUwq3V2oio46bvHiMMoZSGCaTv4yiKOliFOMYr9NAOSTZ ++8mOI24dNXI9XqQ7ZA8m4uKmgHZQUIUUlx693uRI2Wmk/Y5XEBoL2+XdA5KalO+36 ++27YXpdyU3GiMCOtSBLWNfBxXw6oKdNUp+8o/fYrmQnBxuGgmVlcZEmjhrIGXaCH1 ++iDeWIFqaM/S+DXMF3bgqvqRZq1U2RwT2oxapAuaG/0I5JaKKpb3HqMCXfOUxpFPk ++zgUYpHatUcePG/94K8N8CRjnJ+l83H5PewcAEQEAAYkBHwQYAQIACQUCVSvRSgIb ++DAAKCRCtilKP7ESIHrrcCACc6UTZzVGbVq9pXSz2Bw2xQpAEAhnnedPgfXwEJMM0 ++24bMUNsyJcQZAW1d5KfJYNAihOfse3oDQ/hJAycTK3GAHsPfljEQjWGn27eC8Fxu ++mHpfNpxbTirChfepCNctZG818Hp2v+K4X/PjyQMQ6J5H9oinnlasVQ6wzdZifnWm ++7E5OL0NV/ni9xqq4fC5y5qxNBeYVmHUF4H0E3VOuCbESAOnUDpCo998Dc68eZEmV ++f3IMukvvnxM9VOZQSnp7J/kkhPB5fim2z2qrlJK9N+tBjAMugxtnAV2fIaZYTiba ++SnN2hheFd9Y0nMmWbwRqFtwMG1m/tS3JlD52Rpwzk59B ++=WFoi ++-----END PGP PUBLIC KEY BLOCK----- +diff --git a/data/pki/Makefile.am b/data/pki/Makefile.am +new file mode 100644 +index 0000000000..17dcca7ab2 +--- /dev/null ++++ b/data/pki/Makefile.am +@@ -0,0 +1,4 @@ ++pkidir = $(sysconfdir)/pki/fwupd ++dist_pki_DATA = GPG-KEY-Hughski-Limited ++ ++-include $(top_srcdir)/git.mk +diff --git a/data/tests/Makefile.am b/data/tests/Makefile.am +index d6347b2d69..0f4da014ff 100644 +--- a/data/tests/Makefile.am ++++ b/data/tests/Makefile.am +@@ -1,4 +1,7 @@ +-test_files = colorhug-als-3.0.2.cab ++test_files = \ ++ colorhug-als-3.0.2.cab \ ++ firmware.bin \ ++ pki/GPG-KEY-Hughski-Limited + + EXTRA_DIST = $(test_files) + +diff --git a/data/tests/firmware.bin b/data/tests/firmware.bin +new file mode 100644 +index 0000000000000000000000000000000000000000..c3f129f25766c71fb82a420de7945d78d4460add +GIT binary patch +literal 8000 +zcmeHMd3aREk+0T_G>wpi5C|mDzzh(Wd)|YA57`+V_9l?T>-gY^gAE3AnN5smbiw*U +zSq$b2(Fzd`2{2%JlaF{YHi=nZ*b)Ma6A}zYh8PTXzy{+GtXnoQ+F$j1BSswgcK?s& +z>(^IRb#--Bb#eoi{fwMlGvgx +zCDNXzQChV=+6j2{1^NYMpXcNI1?X2}{ +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 +z^_=u3735R|ejUhzJ?2Z!3^viXl}$_#jl +z!Dj={4Xp?FY@5(WXwl2q-R{5!egAML?Gmp*LnUSn%_vnm#a7D^lHrX@A=fe$H33R- +z%TP;!6SD&Dv!AIO`gP!||F$--c6IF=QREy2N0QRa +zG+^)apTQN%$8uI8!^|NtpZxmA*^O;Z?W09(oT-h5aT@vTYeoLsIGbk515ARNjY>;a +zG?`C}^EdU~vz8mo?tvUQ8o3Snv?6$Q9lGH<@p{lKBA^|WOo3yZ*q8GgoRJ8q?8JDL +Oi*/ + FWUPD_ERROR_LAST + } FwupdError; +diff --git a/src/Makefile.am b/src/Makefile.am +index 55594de273..d7d9df3d19 100644 +--- a/src/Makefile.am ++++ b/src/Makefile.am +@@ -11,6 +11,7 @@ AM_CPPFLAGS = \ + $(PIE_CFLAGS) \ + $(POLKIT_CFLAGS) \ + $(UEFI_CFLAGS) \ ++ $(GPGME_CFLAGS) \ + -I$(top_srcdir) \ + -I$(top_builddir) \ + -I$(top_srcdir)/libfwupd \ +@@ -80,6 +81,8 @@ fwupd_SOURCES = \ + fu-debug.h \ + fu-device.c \ + fu-device.h \ ++ fu-keyring.c \ ++ fu-keyring.h \ + fu-pending.c \ + fu-pending.h \ + fu-provider.c \ +@@ -109,6 +112,7 @@ fwupd_LDADD = \ + $(GUDEV_LIBS) \ + $(POLKIT_LIBS) \ + $(SQLITE_LIBS) \ ++ $(GPGME_LIBS) \ + $(UEFI_LIBS) + + fwupd_LDFLAGS = \ +@@ -133,6 +137,8 @@ fu_self_test_SOURCES = \ + fu-cab.h \ + fu-device.c \ + fu-device.h \ ++ fu-keyring.c \ ++ fu-keyring.h \ + fu-pending.c \ + fu-pending.h \ + fu-provider.c \ +@@ -146,6 +152,7 @@ fu_self_test_LDADD = \ + $(APPSTREAM_GLIB_LIBS) \ + $(SQLITE_LIBS) \ + $(GCAB_LIBS) \ ++ $(GPGME_LIBS) \ + $(GLIB_LIBS) + + fu_self_test_CFLAGS = $(WARNINGFLAGS_C) +diff --git a/src/fu-keyring.c b/src/fu-keyring.c +new file mode 100644 +index 0000000000..6e4855d953 +--- /dev/null ++++ b/src/fu-keyring.c +@@ -0,0 +1,368 @@ ++/* -*- 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 "fu-cleanup.h" ++#include "fu-keyring.h" ++ ++static void fu_keyring_finalize (GObject *object); ++ ++#define FU_KEYRING_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), FU_TYPE_KEYRING, FuKeyringPrivate)) ++ ++/** ++ * FuKeyringPrivate: ++ * ++ * Private #FuKeyring data ++ **/ ++struct _FuKeyringPrivate ++{ ++ gpgme_ctx_t ctx; ++}; ++ ++G_DEFINE_TYPE (FuKeyring, fu_keyring, G_TYPE_OBJECT) ++ ++/** ++ * fu_keyring_setup: ++ **/ ++static gboolean ++fu_keyring_setup (FuKeyring *keyring, GError **error) ++{ ++ gpgme_error_t rc; ++ ++ g_return_val_if_fail (FU_IS_KEYRING (keyring), FALSE); ++ ++ if (keyring->priv->ctx != NULL) ++ return TRUE; ++ ++ /* check version */ ++ gpgme_check_version (NULL); ++ ++ /* startup gpgme */ ++ rc = gpg_err_init (); ++ if (rc != GPG_ERR_NO_ERROR) { ++ g_set_error (error, ++ FWUPD_ERROR, ++ FWUPD_ERROR_INTERNAL, ++ "failed to startup GPG: %s", ++ gpgme_strerror (rc)); ++ return FALSE; ++ } ++ ++ /* create a new GPG context */ ++ rc = gpgme_new (&keyring->priv->ctx); ++ if (rc != GPG_ERR_NO_ERROR) { ++ g_set_error (error, ++ FWUPD_ERROR, ++ FWUPD_ERROR_INTERNAL, ++ "failed to create context: %s", ++ gpgme_strerror (rc)); ++ return FALSE; ++ } ++ ++ /* set the protocol */ ++ rc = gpgme_set_protocol (keyring->priv->ctx, GPGME_PROTOCOL_OpenPGP); ++ if (rc != GPG_ERR_NO_ERROR) { ++ g_set_error (error, ++ FWUPD_ERROR, ++ FWUPD_ERROR_INTERNAL, ++ "failed to set protocol: %s", ++ gpgme_strerror (rc)); ++ return FALSE; ++ } ++ ++ /* enable armor mode */ ++ gpgme_set_armor (keyring->priv->ctx, TRUE); ++ ++ return TRUE; ++} ++ ++/** ++ * fu_keyring_add_public_key: ++ **/ ++gboolean ++fu_keyring_add_public_key (FuKeyring *keyring, const gchar *filename, GError **error) ++{ ++ gboolean ret = TRUE; ++ gpgme_data_t data = NULL; ++ gpgme_error_t rc; ++ ++ g_return_val_if_fail (FU_IS_KEYRING (keyring), FALSE); ++ g_return_val_if_fail (filename != NULL, FALSE); ++ ++ /* import public key */ ++ rc = gpgme_data_new_from_file (&data, filename, 1); ++ if (rc != GPG_ERR_NO_ERROR) { ++ ret = FALSE; ++ g_set_error (error, ++ FWUPD_ERROR, ++ FWUPD_ERROR_INTERNAL, ++ "failed to load %s: %s", ++ filename, gpgme_strerror (rc)); ++ goto out; ++ } ++ rc = gpgme_op_import (keyring->priv->ctx, data); ++ if (rc != GPG_ERR_NO_ERROR) { ++ ret = FALSE; ++ g_set_error (error, ++ FWUPD_ERROR, ++ FWUPD_ERROR_INTERNAL, ++ "failed to import %s: %s", ++ filename, gpgme_strerror (rc)); ++ goto out; ++ } ++out: ++ gpgme_data_release (data); ++ return ret; ++} ++ ++/** ++ * fu_keyring_add_public_keys: ++ **/ ++gboolean ++fu_keyring_add_public_keys (FuKeyring *keyring, const gchar *dirname, GError **error) ++{ ++ _cleanup_dir_close_ GDir *dir = NULL; ++ ++ g_return_val_if_fail (FU_IS_KEYRING (keyring), FALSE); ++ g_return_val_if_fail (dirname != NULL, FALSE); ++ ++ /* setup context */ ++ if (!fu_keyring_setup (keyring, error)) ++ return FALSE; ++ ++ /* search all the public key files */ ++ dir = g_dir_open (dirname, 0, error); ++ if (dir == NULL) ++ return FALSE; ++ do { ++ const gchar *filename; ++ _cleanup_free_ gchar *path_tmp = NULL; ++ filename = g_dir_read_name (dir); ++ if (filename == NULL) ++ break; ++ path_tmp = g_build_filename (dirname, filename, NULL); ++ if (!fu_keyring_add_public_key (keyring, path_tmp, error)) ++ return FALSE; ++ } while (TRUE); ++ return TRUE; ++} ++ ++/** ++ * fu_keyring_check_signature: ++ **/ ++static gboolean ++fu_keyring_check_signature (gpgme_signature_t signature, GError **error) ++{ ++ gboolean ret = FALSE; ++ ++ /* look at the signature status */ ++ switch (gpgme_err_code (signature->status)) { ++ case GPG_ERR_NO_ERROR: ++ ret = TRUE; ++ break; ++ case GPG_ERR_SIG_EXPIRED: ++ case GPG_ERR_KEY_EXPIRED: ++ g_set_error (error, ++ FWUPD_ERROR, ++ FWUPD_ERROR_SIGNATURE_INVALID, ++ "valid signature '%s' has expired", ++ signature->fpr); ++ break; ++ case GPG_ERR_CERT_REVOKED: ++ g_set_error (error, ++ FWUPD_ERROR, ++ FWUPD_ERROR_SIGNATURE_INVALID, ++ "valid signature '%s' has been revoked", ++ signature->fpr); ++ break; ++ case GPG_ERR_BAD_SIGNATURE: ++ g_set_error (error, ++ FWUPD_ERROR, ++ FWUPD_ERROR_SIGNATURE_INVALID, ++ "'%s' is not a valid signature", ++ signature->fpr); ++ break; ++ case GPG_ERR_NO_PUBKEY: ++ g_set_error (error, ++ FWUPD_ERROR, ++ FWUPD_ERROR_SIGNATURE_INVALID, ++ "Could not check signature '%s' as no public key", ++ signature->fpr); ++ break; ++ default: ++ g_set_error (error, ++ FWUPD_ERROR, ++ FWUPD_ERROR_SIGNATURE_INVALID, ++ "gpgme failed to verify signature '%s'", ++ signature->fpr); ++ break; ++ } ++ return ret; ++} ++ ++/** ++ * fu_keyring_verify_file: ++ **/ ++gboolean ++fu_keyring_verify_file (FuKeyring *keyring, ++ const gchar *filename, ++ const gchar *signature, ++ GError **error) ++{ ++ gboolean has_header; ++ gboolean ret = TRUE; ++ gpgme_data_t data = NULL; ++ gpgme_data_t sig = NULL; ++ gpgme_error_t rc; ++ gpgme_signature_t s; ++ gpgme_verify_result_t result; ++ _cleanup_string_free_ GString *sig_v1 = NULL; ++ ++ g_return_val_if_fail (FU_IS_KEYRING (keyring), FALSE); ++ g_return_val_if_fail (filename != NULL, FALSE); ++ g_return_val_if_fail (signature != NULL, FALSE); ++ ++ /* setup context */ ++ if (!fu_keyring_setup (keyring, error)) ++ return FALSE; ++ ++ /* has header already */ ++ has_header = g_strstr_len (signature, -1, "BEGIN PGP SIGNATURE") != NULL; ++ ++ /* load file data */ ++ rc = gpgme_data_new_from_file (&data, filename, 1); ++ if (rc != GPG_ERR_NO_ERROR) { ++ ret = FALSE; ++ g_set_error (error, ++ FWUPD_ERROR, ++ FWUPD_ERROR_INTERNAL, ++ "failed to load %s: %s", ++ filename, gpgme_strerror (rc)); ++ goto out; ++ } ++ ++ /* load signature */ ++ sig_v1 = g_string_new (""); ++ if (!has_header) { ++ g_string_append (sig_v1, "-----BEGIN PGP SIGNATURE-----\n"); ++ g_string_append (sig_v1, "Version: GnuPG v1\n\n"); ++ } ++ g_string_append_printf (sig_v1, "%s\n", signature); ++ if (!has_header) ++ g_string_append (sig_v1, "-----END PGP SIGNATURE-----\n"); ++ rc = gpgme_data_new_from_mem (&sig, sig_v1->str, sig_v1->len, 0); ++ if (rc != GPG_ERR_NO_ERROR) { ++ ret = FALSE; ++ g_set_error (error, ++ FWUPD_ERROR, ++ FWUPD_ERROR_INTERNAL, ++ "failed to load signature %s: %s", ++ signature, gpgme_strerror (rc)); ++ goto out; ++ } ++ ++ /* verify */ ++ rc = gpgme_op_verify (keyring->priv->ctx, sig, data, NULL); ++ if (rc != GPG_ERR_NO_ERROR) { ++ ret = FALSE; ++ g_set_error (error, ++ FWUPD_ERROR, ++ FWUPD_ERROR_INTERNAL, ++ "failed to verify %s: %s", ++ filename, gpgme_strerror (rc)); ++ goto out; ++ } ++ ++ ++ /* verify the result */ ++ result = gpgme_op_verify_result (keyring->priv->ctx); ++ if (result == NULL) { ++ ret = FALSE; ++ g_set_error_literal (error, ++ FWUPD_ERROR, ++ FWUPD_ERROR_INTERNAL, ++ "no result record from libgpgme"); ++ goto out; ++ } ++ ++ /* look at each signature */ ++ for (s = result->signatures; s != NULL ; s = s->next ) { ++ ret = fu_keyring_check_signature (s, error); ++ if (!ret) ++ goto out; ++ } ++out: ++ if (data != NULL) ++ gpgme_data_release (data); ++ if (sig != NULL) ++ gpgme_data_release (sig); ++ return ret; ++} ++ ++/** ++ * fu_keyring_class_init: ++ **/ ++static void ++fu_keyring_class_init (FuKeyringClass *klass) ++{ ++ GObjectClass *object_class = G_OBJECT_CLASS (klass); ++ object_class->finalize = fu_keyring_finalize; ++ g_type_class_add_private (klass, sizeof (FuKeyringPrivate)); ++} ++ ++/** ++ * fu_keyring_init: ++ **/ ++static void ++fu_keyring_init (FuKeyring *keyring) ++{ ++ keyring->priv = FU_KEYRING_GET_PRIVATE (keyring); ++} ++ ++/** ++ * fu_keyring_finalize: ++ **/ ++static void ++fu_keyring_finalize (GObject *object) ++{ ++ FuKeyring *keyring = FU_KEYRING (object); ++ FuKeyringPrivate *priv = keyring->priv; ++ ++ if (priv->ctx != NULL) ++ gpgme_release (priv->ctx); ++ ++ G_OBJECT_CLASS (fu_keyring_parent_class)->finalize (object); ++} ++ ++/** ++ * fu_keyring_new: ++ **/ ++FuKeyring * ++fu_keyring_new (void) ++{ ++ FuKeyring *keyring; ++ keyring = g_object_new (FU_TYPE_KEYRING, NULL); ++ return FU_KEYRING (keyring); ++} +diff --git a/src/fu-keyring.h b/src/fu-keyring.h +new file mode 100644 +index 0000000000..75cd6fb788 +--- /dev/null ++++ b/src/fu-keyring.h +@@ -0,0 +1,70 @@ ++/* -*- 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_KEYRING_H ++#define __FU_KEYRING_H ++ ++#include ++#include ++ ++G_BEGIN_DECLS ++ ++#define FU_TYPE_KEYRING (fu_keyring_get_type ()) ++#define FU_KEYRING(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), FU_TYPE_KEYRING, FuKeyring)) ++#define FU_KEYRING_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), FU_TYPE_KEYRING, FuKeyringClass)) ++#define FU_IS_KEYRING(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), FU_TYPE_KEYRING)) ++#define FU_IS_KEYRING_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), FU_TYPE_KEYRING)) ++#define FU_KEYRING_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), FU_TYPE_KEYRING, FuKeyringClass)) ++#define FU_KEYRING_ERROR fu_keyring_error_quark() ++ ++typedef struct _FuKeyringPrivate FuKeyringPrivate; ++typedef struct _FuKeyring FuKeyring; ++typedef struct _FuKeyringClass FuKeyringClass; ++ ++struct _FuKeyring ++{ ++ GObject parent; ++ FuKeyringPrivate *priv; ++}; ++ ++struct _FuKeyringClass ++{ ++ GObjectClass parent_class; ++}; ++ ++GType fu_keyring_get_type (void); ++FuKeyring *fu_keyring_new (void); ++ ++gboolean fu_keyring_add_public_keys (FuKeyring *keyring, ++ const gchar *dirname, ++ GError **error); ++gboolean fu_keyring_add_public_key (FuKeyring *keyring, ++ const gchar *filename, ++ GError **error); ++gboolean fu_keyring_verify_file (FuKeyring *keyring, ++ const gchar *filename, ++ const gchar *signature, ++ GError **error); ++ ++G_END_DECLS ++ ++#endif /* __FU_KEYRING_H */ ++ +diff --git a/src/fu-self-test.c b/src/fu-self-test.c +index 9466934242..b8bd393b6c 100644 +--- a/src/fu-self-test.c ++++ b/src/fu-self-test.c +@@ -28,6 +28,7 @@ + + #include "fu-cab.h" + #include "fu-cleanup.h" ++#include "fu-keyring.h" + #include "fu-pending.h" + #include "fu-provider-fake.h" + +@@ -287,6 +288,45 @@ fu_pending_func (void) + g_clear_error (&error); + } + ++static void ++fu_keyring_func (void) ++{ ++ gboolean ret; ++ _cleanup_error_free_ GError *error = NULL; ++ _cleanup_free_ gchar *fw_fail = NULL; ++ _cleanup_free_ gchar *fw_pass = NULL; ++ _cleanup_free_ gchar *pki_dir = NULL; ++ _cleanup_object_unref_ FuKeyring *keyring = NULL; ++ const gchar *sig = ++ "iQEcBAABAgAGBQJVK9RSAAoJEBesuo36lw4XvmoH/3tJL5wVRN+rsvoo/FMc3w4g" ++ "I7rizJNIgQ04WVTREX6tRZJfxYzGAaeokVeqah2JUC4u1j22BDkoG/Fs+/2/Z/OP" ++ "PTxMoiEzfzryWpVwt20As+H9CmMZGdCfvKgnWiosAENCzE7JE1miJ4YvTpRtdPMh" ++ "erz8DqLTFAfr72aimf5hBs8ZFkBGPGjljdTDv78hk2WDep5E1+1swGoFbhDcXyih" ++ "8GZjSLP7XkKo23/p6odCJD3SkkDE7jIUMA8GrTHHXIhF41UsriKx2ERYoau5k3cX" ++ "OdK3/cRQ6BeuSBMLr7hUpa0RwlKUKex/I7+p/T9Ohk4lNnGS7GpE45RbpflK1VQ=" ++ "=0D8+"; ++ ++ /* add test keys to keyring */ ++ keyring = fu_keyring_new (); ++ pki_dir = fu_test_get_filename ("pki"); ++ ret = fu_keyring_add_public_keys (keyring, pki_dir, &error); ++ g_assert_no_error (error); ++ g_assert (ret); ++ ++ /* verify */ ++ fw_pass = fu_test_get_filename ("firmware.bin"); ++ ret = fu_keyring_verify_file (keyring, fw_pass, sig, &error); ++ g_assert_no_error (error); ++ g_assert (ret); ++ ++ /* verify will fail */ ++ fw_fail = fu_test_get_filename ("colorhug-als-3.0.2.cab"); ++ ret = fu_keyring_verify_file (keyring, fw_fail, sig, &error); ++ g_assert_error (error, FWUPD_ERROR, FWUPD_ERROR_SIGNATURE_INVALID); ++ g_assert (!ret); ++ g_clear_error (&error); ++} ++ + int + main (int argc, char **argv) + { +@@ -299,6 +339,7 @@ main (int argc, char **argv) + 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/keyring", fu_keyring_func); + return g_test_run (); + } + diff --git a/839b89f45a38b2373bf5836337a33f450aaab72e.patch b/839b89f45a38b2373bf5836337a33f450aaab72e.patch new file mode 100644 index 0000000..f6b9610 --- /dev/null +++ b/839b89f45a38b2373bf5836337a33f450aaab72e.patch @@ -0,0 +1,111 @@ +From 839b89f45a38b2373bf5836337a33f450aaab72e Mon Sep 17 00:00:00 2001 +From: Richard Hughes +Date: Thu, 28 May 2020 10:41:23 +0100 +Subject: [PATCH] Validate that gpgme_op_verify_result() returned at least one + signature + +If a detached signature is actually a PGP message, gpgme_op_verify() returns +the rather perplexing GPG_ERR_NO_ERROR, and then gpgme_op_verify_result() +builds an empty list. + +Explicitly check for no signatures present to avoid returning a JcatResult with +no timestamp and an empty authority. + +Many thanks to Justin Steven for the discovery and +coordinated disclosure of this issue. Fixes CVE-2020-10759 +--- + libjcat/jcat-gpg-engine.c | 7 +++++ + libjcat/jcat-self-test.c | 55 +++++++++++++++++++++++++++++++++++++++ + 2 files changed, 62 insertions(+) + +diff --git a/libjcat/jcat-gpg-engine.c b/libjcat/jcat-gpg-engine.c +index 0812a62..bd44dba 100644 +--- a/libjcat/jcat-gpg-engine.c ++++ b/libjcat/jcat-gpg-engine.c +@@ -267,6 +267,13 @@ jcat_gpg_engine_pubkey_verify (JcatEngine *engine, + "no result record from libgpgme"); + return NULL; + } ++ if (result->signatures == NULL) { ++ g_set_error_literal (error, ++ G_IO_ERROR, ++ G_IO_ERROR_FAILED, ++ "no signatures from libgpgme"); ++ return NULL; ++ } + + /* look at each signature */ + for (s = result->signatures; s != NULL ; s = s->next ) { +diff --git a/libjcat/jcat-self-test.c b/libjcat/jcat-self-test.c +index d79a3a9..fd4295e 100644 +--- a/libjcat/jcat-self-test.c ++++ b/libjcat/jcat-self-test.c +@@ -393,6 +393,60 @@ jcat_gpg_engine_func (void) + #endif + } + ++static void ++jcat_gpg_engine_msg_func (void) ++{ ++#ifdef ENABLE_GPG ++ g_autofree gchar *fn = NULL; ++ g_autofree gchar *pki_dir = NULL; ++ g_autoptr(GBytes) data = NULL; ++ g_autoptr(GBytes) data_sig = NULL; ++ g_autoptr(GError) error = NULL; ++ g_autoptr(JcatContext) context = jcat_context_new (); ++ g_autoptr(JcatEngine) engine = NULL; ++ g_autoptr(JcatResult) result = NULL; ++ const gchar *sig = ++ "-----BEGIN PGP MESSAGE-----\n" ++ "owGbwMvMwMEovmZX76/pfOKMp0WSGOLOX3/ikZqTk6+jUJ5flJOiyNXJaMzCwMjB\n" ++ "ICumyCJmt5VRUil28/1+z1cwbaxMID0MXJwCMJG4RxwMLUYXDkUad34I3vrT8+X2\n" ++ "m+ZyHyMWnTiQYaQb/eLJGqbiAJc5Jr4a/PPqHNi7auwzGsKsljebabjtnJRzpDr0\n" ++ "YvwrnmmWLJUnTzjM3MH5Kn+RzqXkywsYdk9yD2OUdLy736CiemFMdcuF02lOZvPU\n" ++ "HaTKl76wW62QH8Lr8yGMQ1Xgc6nC2ZwUhvctky7NOZtc1T477uBTL81p31ZmaIUJ\n" ++ "paS8uWZl8UzX5sFsqQi37G1TbDc8Cm+oU/yRkFj2pLBzw367ncsa4n7EqEWu1yrN\n" ++ "yD39LUeErePdqfKCG+xhL6WkWt5ZJ/6//XnjouXhl5Z4tWspT49MtNp5d3aDQ43c\n" ++ "mnbresn6A7KMZgdOiwIA\n" ++ "=a9ui\n" ++ "-----END PGP MESSAGE-----\n"; ++ ++ /* set up context */ ++ jcat_context_set_keyring_path (context, "/tmp/libjcat-self-test/var"); ++ pki_dir = g_test_build_filename (G_TEST_DIST, "pki", NULL); ++ jcat_context_add_public_keys (context, pki_dir); ++ ++ /* get engine */ ++ engine = jcat_context_get_engine (context, JCAT_BLOB_KIND_GPG, &error); ++ g_assert_no_error (error); ++ g_assert_nonnull (engine); ++ g_assert_cmpint (jcat_engine_get_kind (engine), ==, JCAT_BLOB_KIND_GPG); ++ g_assert_cmpint (jcat_engine_get_verify_kind (engine), ==, JCAT_ENGINE_VERIFY_KIND_SIGNATURE); ++ ++ /* verify with GnuPG, which should fail as the signature is not a ++ * detached signature at all, but gnupg stabs us in the back by returning ++ * success from gpgme_op_verify() with an empty list of signatures */ ++ fn = g_test_build_filename (G_TEST_DIST, "colorhug", "firmware.bin", NULL); ++ data = jcat_get_contents_bytes (fn, &error); ++ g_assert_no_error (error); ++ g_assert_nonnull (data); ++ data_sig = g_bytes_new_static (sig, strlen (sig)); ++ result = jcat_engine_pubkey_verify (engine, data, data_sig, ++ JCAT_VERIFY_FLAG_NONE, &error); ++ g_assert_error (error, G_IO_ERROR, G_IO_ERROR_FAILED); ++ g_assert_null (result); ++#else ++ g_test_skip ("no GnuPG support enabled"); ++#endif ++} ++ + static void + jcat_pkcs7_engine_func (void) + { +@@ -753,6 +807,7 @@ main (int argc, char **argv) + g_test_add_func ("/jcat/engine{sha1}", jcat_sha1_engine_func); + g_test_add_func ("/jcat/engine{sha256}", jcat_sha256_engine_func); + g_test_add_func ("/jcat/engine{gpg}", jcat_gpg_engine_func); ++ g_test_add_func ("/jcat/engine{gpg-msg}", jcat_gpg_engine_msg_func); + g_test_add_func ("/jcat/engine{pkcs7}", jcat_pkcs7_engine_func); + g_test_add_func ("/jcat/engine{pkcs7-self-signed}", jcat_pkcs7_engine_self_signed_func); + g_test_add_func ("/jcat/context{verify-blob}", jcat_context_verify_blob_func); diff --git a/libxmlb.spec b/libxmlb.spec index 4de0c4a..6b4e0cf 100644 --- a/libxmlb.spec +++ b/libxmlb.spec @@ -1,6 +1,6 @@ Name: libxmlb Version: 0.3.7 -Release: 1 +Release: 1.h1 Summary: A library to help create and query binary XML blobs License: LGPLv2+ URL: https://github.com/hughsie/libxmlb @@ -60,8 +60,23 @@ This package contains the development files for %{name}. %{_datadir}/*tests/%{name} %changelog +* Fri Jul 15 2022 jiangpeng +- Type:CVE +- SUG :NO +- DESC : fix CVE-2020-10759 + + * Fri Apr 15 2022 lin zhang - 0.3.7-1 - Update to 0.3.7 * Mon Nov 25 2019 openEuler Buildteam - 0.1.13-2 - Package init + +%Patch +# CVE-2020-10759 +Patch1 21f2d12fccef63b8aaa99ec53278ce18250b0444.patch +Patch2 839b89f45a38b2373bf5836337a33f450aaab72e.patch +Patch3 36a889034c3d34ae4ac4530ea7b6b16e82476fae.patch +%patch1 -p1 +%patch2 -p1 +%patch3 -p1 -- Gitee