mirror of
https://git.proxmox.com/git/fwupd
synced 2025-08-11 20:24:04 +00:00
Use Jcat files in firmware archives and for metadata
A Jcat file can be used to store GPG, PKCS-7 and SHA-256 checksums for multiple files. This allows us to sign a firmware or metadata multiple times (perhaps by the OEM and also then the LVFS) which further decentralizes the trust model of the LVFS. The Jcat format was chosen as the Microsoft catalog format is nonfree and not documented. We also don't want to modify an existing .cat file created from WU as this may make it unsuitable to use on Windows. More information can be found here: https://github.com/hughsie/libjcat
This commit is contained in:
parent
86b0bae0c4
commit
d5aab65f30
@ -6,6 +6,7 @@ ENV LC_ALL en_US.UTF-8
|
||||
RUN echo fubar > /etc/machine-id
|
||||
RUN dnf -y update
|
||||
RUN echo fubar > /etc/machine-id
|
||||
RUN dnf -y install https://kojipkgs.fedoraproject.org//packages/libjcat/0.1.0/1.fc31/x86_64/libjcat-0.1.0-1.fc31.x86_64.rpm https://kojipkgs.fedoraproject.org//packages/libjcat/0.1.0/1.fc31/x86_64/libjcat-devel-0.1.0-1.fc31.x86_64.rpm
|
||||
%%%INSTALL_DEPENDENCIES_COMMAND%%%
|
||||
RUN dnf -y update glib2 glib2-devel --releasever=32
|
||||
RUN mkdir /build
|
||||
|
@ -15,7 +15,6 @@ meson .. \
|
||||
--libexecdir=$target \
|
||||
--bindir=$target \
|
||||
-Dbuild=standalone \
|
||||
-Dgpg=false \
|
||||
-Dplugin_coreboot=false \
|
||||
-Dplugin_flashrom=false \
|
||||
-Dplugin_uefi=false \
|
||||
@ -38,6 +37,9 @@ meson .. \
|
||||
-Dgcab:tests=false \
|
||||
-Dlibxmlb:introspection=false \
|
||||
-Dlibxmlb:gtkdoc=false \
|
||||
-Dlibjcat:man=false \
|
||||
-Dlibjcat:gpg=false \
|
||||
-Dlibjcat:introspection=false \
|
||||
-Dgudev=false $@
|
||||
ninja -v
|
||||
|
||||
|
@ -44,6 +44,8 @@ lintian ../*changes \
|
||||
--suppress-tags no-symbols-control-file \
|
||||
--suppress-tags gzip-file-is-not-multi-arch-same-safe \
|
||||
--suppress-tags binary-is-wrong-architecture \
|
||||
--suppress-tags missing-dependency-on-libc \
|
||||
--suppress-tags arch-dependent-file-not-in-arch-specific-directory \
|
||||
--allow-root
|
||||
|
||||
#if invoked outside of CI
|
||||
|
@ -633,6 +633,11 @@
|
||||
<package variant="x86_64" />
|
||||
</distro>
|
||||
</dependency>
|
||||
<dependency type="build" id="libjcat-dev">
|
||||
<distro id="fedora">
|
||||
<package>libjcat-devel</package>
|
||||
</distro>
|
||||
</dependency>
|
||||
<dependency type="build" id="libarchive-dev">
|
||||
<distro id="centos">
|
||||
<package>libarchive-devel</package>
|
||||
|
@ -46,6 +46,9 @@ override_dh_auto_configure:
|
||||
else \
|
||||
export UEFI="-Dplugin_uefi=false -Dplugin_redfish=false -Dplugin_nvme=false"; \
|
||||
fi; \
|
||||
if [ ! -z "$$CI" ]; then \
|
||||
export CI="-Dlibjcat:introspection=false $$CI"; \
|
||||
fi; \
|
||||
dh_auto_configure -- $$UEFI $$DELL $$FLASHROM $$CI -Dplugin_dummy=true -Dgtkdoc=true
|
||||
|
||||
override_dh_install:
|
||||
@ -56,6 +59,16 @@ override_dh_install:
|
||||
if [ -d debian/tmp/usr/libexec/fwupd/efi/ ]; then \
|
||||
dh_install -pfwupd usr/libexec/fwupd/efi ;\
|
||||
fi
|
||||
#libjcat built as a subproject for CI
|
||||
if [ ! -z "$$CI" ]; then \
|
||||
dh_install -plibfwupd-dev usr/include/libjcat-1; \
|
||||
dh_install -plibfwupd2 usr/lib/*/libjcat.so.*; \
|
||||
dh_install -plibfwupd-dev usr/lib/*/libjcat.so; \
|
||||
dh_install -plibfwupd-dev usr/lib/*/pkgconfig/jcat.pc; \
|
||||
dh_install -plibfwupd-dev usr/libexec/jcat-tool; \
|
||||
dh_install -plibfwupd-dev usr/share/gir-1.0/Jcat*; \
|
||||
dh_install -pfwupd-tests usr/libexec/installed-tests/libjcat; \
|
||||
fi
|
||||
dh_missing -a --fail-missing
|
||||
|
||||
#this is placed in fwupd-tests
|
||||
|
@ -60,11 +60,13 @@ Section "fwupd"
|
||||
|
||||
# fwupd
|
||||
File "dfu-tool.exe"
|
||||
File "jcat-tool.exe"
|
||||
File "fwupdtool.exe"
|
||||
File "libfwupd-2.dll"
|
||||
File "libfwupdplugin-1.dll"
|
||||
File "libgcab-1.0-0.dll"
|
||||
File "libxmlb-1.dll"
|
||||
File "libjcat-1.dll"
|
||||
SetOutPath "$INSTDIR\fwupd-plugins-3"
|
||||
File /r "fwupd-plugins-3/libfu_plugin_*.dll"
|
||||
SetOutPath "$INSTDIR\etc\fwupd"
|
||||
|
@ -1,30 +1,26 @@
|
||||
if get_option('gpg')
|
||||
install_data([
|
||||
'GPG-KEY-Hughski-Limited',
|
||||
'GPG-KEY-Linux-Foundation-Firmware',
|
||||
'GPG-KEY-Linux-Vendor-Firmware-Service',
|
||||
],
|
||||
install_dir : join_paths(sysconfdir, 'pki', 'fwupd')
|
||||
)
|
||||
install_data([
|
||||
'GPG-KEY-Hughski-Limited',
|
||||
'GPG-KEY-Linux-Foundation-Firmware',
|
||||
'GPG-KEY-Linux-Vendor-Firmware-Service',
|
||||
],
|
||||
install_dir : join_paths(sysconfdir, 'pki', 'fwupd')
|
||||
)
|
||||
|
||||
install_data([
|
||||
'GPG-KEY-Linux-Foundation-Metadata',
|
||||
'GPG-KEY-Linux-Vendor-Firmware-Service',
|
||||
],
|
||||
install_dir : join_paths(sysconfdir, 'pki', 'fwupd-metadata')
|
||||
)
|
||||
endif
|
||||
install_data([
|
||||
'GPG-KEY-Linux-Foundation-Metadata',
|
||||
'GPG-KEY-Linux-Vendor-Firmware-Service',
|
||||
],
|
||||
install_dir : join_paths(sysconfdir, 'pki', 'fwupd-metadata')
|
||||
)
|
||||
|
||||
if get_option('pkcs7')
|
||||
install_data([
|
||||
'LVFS-CA.pem',
|
||||
],
|
||||
install_dir : join_paths(sysconfdir, 'pki', 'fwupd')
|
||||
)
|
||||
install_data([
|
||||
'LVFS-CA.pem',
|
||||
],
|
||||
install_dir : join_paths(sysconfdir, 'pki', 'fwupd-metadata')
|
||||
)
|
||||
endif
|
||||
install_data([
|
||||
'LVFS-CA.pem',
|
||||
],
|
||||
install_dir : join_paths(sysconfdir, 'pki', 'fwupd')
|
||||
)
|
||||
install_data([
|
||||
'LVFS-CA.pem',
|
||||
],
|
||||
install_dir : join_paths(sysconfdir, 'pki', 'fwupd-metadata')
|
||||
)
|
||||
|
||||
|
@ -3,7 +3,6 @@
|
||||
# this remote provides metadata and firmware marked as 'testing' from the LVFS
|
||||
Enabled=false
|
||||
Title=Linux Vendor Firmware Service (testing)
|
||||
Keyring=gpg
|
||||
MetadataURI=https://cdn.fwupd.org/downloads/firmware-testing.xml.gz
|
||||
ReportURI=https://fwupd.org/lvfs/firmware/report
|
||||
Username=
|
||||
|
@ -3,7 +3,6 @@
|
||||
# this remote provides metadata and firmware marked as 'stable' from the LVFS
|
||||
Enabled=true
|
||||
Title=Linux Vendor Firmware Service
|
||||
Keyring=gpg
|
||||
MetadataURI=https://cdn.fwupd.org/downloads/firmware.xml.gz
|
||||
ReportURI=https://fwupd.org/lvfs/firmware/report
|
||||
OrderBefore=fwupd
|
||||
|
@ -9,17 +9,3 @@ colorhug_test_firmware = custom_target('colorhug-test-firmware',
|
||||
gcab, '--create', '--nopath', '@OUTPUT@', '@INPUT@',
|
||||
],
|
||||
)
|
||||
|
||||
if get_option('pkcs7')
|
||||
# generate self-signed detached signature
|
||||
colorhug_pkcs7_signature = custom_target('firmware.bin.p7c',
|
||||
input: 'firmware.bin',
|
||||
output: 'firmware.bin.p7c',
|
||||
command: [certtool, '--p7-detached-sign',
|
||||
'--p7-time',
|
||||
'--load-privkey', pkcs7_privkey,
|
||||
'--load-certificate', pkcs7_certificate,
|
||||
'--infile', '@INPUT@',
|
||||
'--outfile', '@OUTPUT@'],
|
||||
)
|
||||
endif
|
||||
|
@ -1,12 +1,4 @@
|
||||
# generate private PKCS7 key
|
||||
certtool = find_program('certtool')
|
||||
pkcs7_privkey = custom_target('test-privkey.pem',
|
||||
output: 'test-privkey.pem',
|
||||
command: [certtool, '--generate-privkey', '--outfile', '@OUTPUT@'],
|
||||
)
|
||||
|
||||
subdir('builder')
|
||||
subdir('pki')
|
||||
subdir('colorhug')
|
||||
subdir('missing-hwid')
|
||||
subdir('multiple-rels')
|
||||
|
@ -1 +0,0 @@
|
||||
../../pki/GPG-KEY-Linux-Vendor-Firmware-Service
|
@ -1 +0,0 @@
|
||||
../../pki/LVFS-CA.pem
|
@ -1,10 +0,0 @@
|
||||
# generate certificate
|
||||
pkcs7_config = join_paths(meson.current_source_dir(), 'test.cfg')
|
||||
pkcs7_certificate = custom_target('test.pem',
|
||||
input: pkcs7_privkey,
|
||||
output: 'test.pem',
|
||||
command: [certtool, '--generate-self-signed',
|
||||
'--template', pkcs7_config,
|
||||
'--load-privkey', '@INPUT@',
|
||||
'--outfile', '@OUTPUT@'],
|
||||
)
|
@ -1,4 +0,0 @@
|
||||
organization = "Hughski Limited"
|
||||
expiration_days = -1
|
||||
email = "info@hughski.com"
|
||||
signing_key
|
@ -401,6 +401,8 @@ fwupd_keyring_kind_from_string (const gchar *keyring_kind)
|
||||
return FWUPD_KEYRING_KIND_GPG;
|
||||
if (g_strcmp0 (keyring_kind, "pkcs7") == 0)
|
||||
return FWUPD_KEYRING_KIND_PKCS7;
|
||||
if (g_strcmp0 (keyring_kind, "jcat") == 0)
|
||||
return FWUPD_KEYRING_KIND_JCAT;
|
||||
return FWUPD_KEYRING_KIND_UNKNOWN;
|
||||
}
|
||||
|
||||
@ -423,6 +425,8 @@ fwupd_keyring_kind_to_string (FwupdKeyringKind keyring_kind)
|
||||
return "gpg";
|
||||
if (keyring_kind == FWUPD_KEYRING_KIND_PKCS7)
|
||||
return "pkcs7";
|
||||
if (keyring_kind == FWUPD_KEYRING_KIND_JCAT)
|
||||
return "jcat";
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
@ -232,6 +232,7 @@ typedef enum {
|
||||
* @FWUPD_KEYRING_KIND_NONE: No verification
|
||||
* @FWUPD_KEYRING_KIND_GPG: Verification using GPG
|
||||
* @FWUPD_KEYRING_KIND_PKCS7: Verification using PKCS7
|
||||
* @FWUPD_KEYRING_KIND_JCAT: Verification using Jcat
|
||||
*
|
||||
* The update state.
|
||||
**/
|
||||
@ -240,6 +241,7 @@ typedef enum {
|
||||
FWUPD_KEYRING_KIND_NONE, /* Since: 0.9.7 */
|
||||
FWUPD_KEYRING_KIND_GPG, /* Since: 0.9.7 */
|
||||
FWUPD_KEYRING_KIND_PKCS7, /* Since: 0.9.7 */
|
||||
FWUPD_KEYRING_KIND_JCAT, /* Since: 1.4.0 */
|
||||
/*< private >*/
|
||||
FWUPD_KEYRING_KIND_LAST
|
||||
} FwupdKeyringKind;
|
||||
|
@ -146,16 +146,6 @@ fwupd_remote_set_filename_source (FwupdRemote *self, const gchar *filename_sourc
|
||||
priv->filename_source = g_strdup (filename_source);
|
||||
}
|
||||
|
||||
static const gchar *
|
||||
fwupd_remote_get_suffix_for_keyring_kind (FwupdKeyringKind keyring_kind)
|
||||
{
|
||||
if (keyring_kind == FWUPD_KEYRING_KIND_GPG)
|
||||
return ".asc";
|
||||
if (keyring_kind == FWUPD_KEYRING_KIND_PKCS7)
|
||||
return ".p7b";
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static SoupURI *
|
||||
fwupd_remote_build_uri (FwupdRemote *self, const gchar *url, GError **error)
|
||||
{
|
||||
@ -231,7 +221,6 @@ static void
|
||||
fwupd_remote_set_metadata_uri (FwupdRemote *self, const gchar *metadata_uri)
|
||||
{
|
||||
FwupdRemotePrivate *priv = GET_PRIVATE (self);
|
||||
const gchar *suffix;
|
||||
g_autoptr(SoupURI) uri = NULL;
|
||||
|
||||
/* build the URI */
|
||||
@ -243,9 +232,8 @@ fwupd_remote_set_metadata_uri (FwupdRemote *self, const gchar *metadata_uri)
|
||||
priv->metadata_uri = g_strdup (metadata_uri);
|
||||
|
||||
/* generate the signature URI too */
|
||||
suffix = fwupd_remote_get_suffix_for_keyring_kind (priv->keyring_kind);
|
||||
if (suffix != NULL)
|
||||
priv->metadata_uri_sig = g_strconcat (metadata_uri, suffix, NULL);
|
||||
if (priv->keyring_kind == FWUPD_KEYRING_KIND_JCAT)
|
||||
priv->metadata_uri_sig = g_strconcat (metadata_uri, ".jcat", NULL);
|
||||
}
|
||||
|
||||
/* note, this has to be set after MetadataURI */
|
||||
@ -311,7 +299,6 @@ static void
|
||||
fwupd_remote_set_filename_cache (FwupdRemote *self, const gchar *filename)
|
||||
{
|
||||
FwupdRemotePrivate *priv = GET_PRIVATE (self);
|
||||
const gchar *suffix;
|
||||
|
||||
g_return_if_fail (FWUPD_IS_REMOTE (self));
|
||||
|
||||
@ -319,10 +306,9 @@ fwupd_remote_set_filename_cache (FwupdRemote *self, const gchar *filename)
|
||||
priv->filename_cache = g_strdup (filename);
|
||||
|
||||
/* create for all remote types */
|
||||
suffix = fwupd_remote_get_suffix_for_keyring_kind (priv->keyring_kind);
|
||||
if (suffix != NULL) {
|
||||
if (priv->keyring_kind == FWUPD_KEYRING_KIND_JCAT) {
|
||||
g_free (priv->filename_cache_sig);
|
||||
priv->filename_cache_sig = g_strconcat (filename, suffix, NULL);
|
||||
priv->filename_cache_sig = g_strconcat (filename, ".jcat", NULL);
|
||||
}
|
||||
}
|
||||
|
||||
@ -375,7 +361,7 @@ fwupd_remote_load_from_filename (FwupdRemote *self,
|
||||
/* get verification type, falling back to GPG */
|
||||
keyring_kind = g_key_file_get_string (kf, group, "Keyring", NULL);
|
||||
if (keyring_kind == NULL) {
|
||||
priv->keyring_kind = FWUPD_KEYRING_KIND_GPG;
|
||||
priv->keyring_kind = FWUPD_KEYRING_KIND_JCAT;
|
||||
} else {
|
||||
priv->keyring_kind = fwupd_keyring_kind_from_string (keyring_kind);
|
||||
if (priv->keyring_kind == FWUPD_KEYRING_KIND_UNKNOWN) {
|
||||
@ -386,6 +372,11 @@ fwupd_remote_load_from_filename (FwupdRemote *self,
|
||||
keyring_kind);
|
||||
return FALSE;
|
||||
}
|
||||
if (priv->keyring_kind == FWUPD_KEYRING_KIND_GPG ||
|
||||
priv->keyring_kind == FWUPD_KEYRING_KIND_PKCS7) {
|
||||
g_debug ("converting Keyring value to Jcat");
|
||||
priv->keyring_kind = FWUPD_KEYRING_KIND_JCAT;
|
||||
}
|
||||
}
|
||||
|
||||
/* all remotes need a URI, even if it's file:// to the cache */
|
||||
|
@ -168,14 +168,14 @@ fwupd_remote_download_func (void)
|
||||
"lvfs",
|
||||
"metadata.xml.gz",
|
||||
NULL);
|
||||
expected_signature = g_strdup_printf ("%s.asc", expected_metadata);
|
||||
expected_signature = g_strdup_printf ("%s.jcat", expected_metadata);
|
||||
fwupd_remote_set_remotes_dir (remote, directory);
|
||||
fn = g_build_filename (FU_SELF_TEST_REMOTES_DIR, "remotes.d", "lvfs.conf", NULL);
|
||||
ret = fwupd_remote_load_from_filename (remote, fn, NULL, &error);
|
||||
g_assert_no_error (error);
|
||||
g_assert (ret);
|
||||
g_assert_cmpint (fwupd_remote_get_kind (remote), ==, FWUPD_REMOTE_KIND_DOWNLOAD);
|
||||
g_assert_cmpint (fwupd_remote_get_keyring_kind (remote), ==, FWUPD_KEYRING_KIND_GPG);
|
||||
g_assert_cmpint (fwupd_remote_get_keyring_kind (remote), ==, FWUPD_KEYRING_KIND_JCAT);
|
||||
g_assert_cmpint (fwupd_remote_get_priority (remote), ==, 0);
|
||||
g_assert (fwupd_remote_get_enabled (remote));
|
||||
g_assert (fwupd_remote_get_metadata_uri (remote) != NULL);
|
||||
@ -209,14 +209,14 @@ fwupd_remote_baseuri_func (void)
|
||||
g_assert_no_error (error);
|
||||
g_assert (ret);
|
||||
g_assert_cmpint (fwupd_remote_get_kind (remote), ==, FWUPD_REMOTE_KIND_DOWNLOAD);
|
||||
g_assert_cmpint (fwupd_remote_get_keyring_kind (remote), ==, FWUPD_KEYRING_KIND_GPG);
|
||||
g_assert_cmpint (fwupd_remote_get_keyring_kind (remote), ==, FWUPD_KEYRING_KIND_JCAT);
|
||||
g_assert_cmpint (fwupd_remote_get_priority (remote), ==, 0);
|
||||
g_assert (fwupd_remote_get_enabled (remote));
|
||||
g_assert_cmpstr (fwupd_remote_get_checksum (remote), ==, NULL);
|
||||
g_assert_cmpstr (fwupd_remote_get_metadata_uri (remote), ==,
|
||||
"https://s3.amazonaws.com/lvfsbucket/downloads/firmware.xml.gz");
|
||||
g_assert_cmpstr (fwupd_remote_get_metadata_uri_sig (remote), ==,
|
||||
"https://s3.amazonaws.com/lvfsbucket/downloads/firmware.xml.gz.asc");
|
||||
"https://s3.amazonaws.com/lvfsbucket/downloads/firmware.xml.gz.jcat");
|
||||
firmware_uri = fwupd_remote_build_firmware_uri (remote, "http://bbc.co.uk/firmware.cab", &error);
|
||||
g_assert_no_error (error);
|
||||
g_assert_cmpstr (firmware_uri, ==, "https://my.fancy.cdn/firmware.cab");
|
||||
@ -245,14 +245,14 @@ fwupd_remote_nopath_func (void)
|
||||
g_assert_no_error (error);
|
||||
g_assert (ret);
|
||||
g_assert_cmpint (fwupd_remote_get_kind (remote), ==, FWUPD_REMOTE_KIND_DOWNLOAD);
|
||||
g_assert_cmpint (fwupd_remote_get_keyring_kind (remote), ==, FWUPD_KEYRING_KIND_GPG);
|
||||
g_assert_cmpint (fwupd_remote_get_keyring_kind (remote), ==, FWUPD_KEYRING_KIND_JCAT);
|
||||
g_assert_cmpint (fwupd_remote_get_priority (remote), ==, 0);
|
||||
g_assert (fwupd_remote_get_enabled (remote));
|
||||
g_assert_cmpstr (fwupd_remote_get_checksum (remote), ==, NULL);
|
||||
g_assert_cmpstr (fwupd_remote_get_metadata_uri (remote), ==,
|
||||
"https://s3.amazonaws.com/lvfsbucket/downloads/firmware.xml.gz");
|
||||
g_assert_cmpstr (fwupd_remote_get_metadata_uri_sig (remote), ==,
|
||||
"https://s3.amazonaws.com/lvfsbucket/downloads/firmware.xml.gz.asc");
|
||||
"https://s3.amazonaws.com/lvfsbucket/downloads/firmware.xml.gz.jcat");
|
||||
firmware_uri = fwupd_remote_build_firmware_uri (remote, "firmware.cab", &error);
|
||||
g_assert_no_error (error);
|
||||
g_assert_cmpstr (firmware_uri, ==, "https://s3.amazonaws.com/lvfsbucket/downloads/firmware.cab");
|
||||
|
@ -14,6 +14,7 @@
|
||||
#include "fu-cabinet.h"
|
||||
#include "fu-common.h"
|
||||
|
||||
#include "fwupd-enums.h"
|
||||
#include "fwupd-error.h"
|
||||
|
||||
struct _FuCabinet {
|
||||
@ -23,6 +24,8 @@ struct _FuCabinet {
|
||||
gchar *container_checksum;
|
||||
XbBuilder *builder;
|
||||
XbSilo *silo;
|
||||
JcatContext *jcat_context;
|
||||
JcatFile *jcat_file;
|
||||
};
|
||||
|
||||
G_DEFINE_TYPE (FuCabinet, fu_cabinet, G_TYPE_OBJECT)
|
||||
@ -37,6 +40,8 @@ fu_cabinet_finalize (GObject *obj)
|
||||
g_object_unref (self->builder);
|
||||
g_free (self->container_checksum);
|
||||
g_object_unref (self->gcab_cabinet);
|
||||
g_object_unref (self->jcat_context);
|
||||
g_object_unref (self->jcat_file);
|
||||
G_OBJECT_CLASS (fu_cabinet_parent_class)->finalize (obj);
|
||||
}
|
||||
|
||||
@ -53,6 +58,8 @@ fu_cabinet_init (FuCabinet *self)
|
||||
self->size_max = 1024 * 1024 * 100;
|
||||
self->gcab_cabinet = gcab_cabinet_new ();
|
||||
self->builder = xb_builder_new ();
|
||||
self->jcat_file = jcat_file_new ();
|
||||
self->jcat_context = jcat_context_new ();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -71,6 +78,24 @@ fu_cabinet_set_size_max (FuCabinet *self, guint64 size_max)
|
||||
self->size_max = size_max;
|
||||
}
|
||||
|
||||
/**
|
||||
* fu_cabinet_set_jcat_context: (skip):
|
||||
* @self: A #FuCabinet
|
||||
* @jcat_context: (nullable): A #JcatContext
|
||||
*
|
||||
* Sets the Jcat context, which is used for setting the trust flags on the
|
||||
* each release in the archive.
|
||||
*
|
||||
* Since: 1.4.0
|
||||
**/
|
||||
void
|
||||
fu_cabinet_set_jcat_context (FuCabinet *self, JcatContext *jcat_context)
|
||||
{
|
||||
g_return_if_fail (FU_IS_CABINET (self));
|
||||
g_return_if_fail (JCAT_IS_CONTEXT (jcat_context));
|
||||
g_set_object (&self->jcat_context, jcat_context);
|
||||
}
|
||||
|
||||
/**
|
||||
* fu_cabinet_get_silo: (skip):
|
||||
* @self: A #FuCabinet
|
||||
@ -111,11 +136,18 @@ fu_cabinet_parse_release (FuCabinet *self, XbNode *release, GError **error)
|
||||
GCabFile *cabfile;
|
||||
GBytes *blob;
|
||||
const gchar *csum_filename = NULL;
|
||||
const gchar *suffixes[] = { "asc", "p7b", "p7c", NULL };
|
||||
g_autofree gchar *basename = NULL;
|
||||
g_autofree gchar *release_key = NULL;
|
||||
g_autoptr(XbNode) csum_tmp = NULL;
|
||||
g_autoptr(XbNode) metadata_trust = NULL;
|
||||
g_autoptr(XbNode) nsize = NULL;
|
||||
g_autoptr(JcatItem) item = NULL;
|
||||
g_autoptr(GBytes) release_flags_blob = NULL;
|
||||
FwupdReleaseFlags release_flags = FWUPD_RELEASE_FLAG_NONE;
|
||||
|
||||
/* we set this with XbBuilderSource before the silo was created */
|
||||
metadata_trust = xb_node_query_first (release, "../../info/metadata_trust", NULL);
|
||||
if (metadata_trust != NULL)
|
||||
release_flags |= FWUPD_RELEASE_FLAG_TRUSTED_METADATA;
|
||||
|
||||
/* ensure we always have a content checksum */
|
||||
csum_tmp = xb_node_query_first (release, "checksum[@target='content']", NULL);
|
||||
@ -148,8 +180,7 @@ fu_cabinet_parse_release (FuCabinet *self, XbNode *release, GError **error)
|
||||
}
|
||||
|
||||
/* set the blob */
|
||||
release_key = g_strdup_printf ("fwupd::ReleaseBlob(%s)", basename);
|
||||
xb_node_set_data (release, release_key, blob);
|
||||
xb_node_set_data (release, "fwupd::FirmwareBlob", blob);
|
||||
|
||||
/* set as metadata if unset, but error if specified and incorrect */
|
||||
nsize = xb_node_query_first (release, "size[@type='installed']", NULL);
|
||||
@ -185,15 +216,38 @@ fu_cabinet_parse_release (FuCabinet *self, XbNode *release, GError **error)
|
||||
}
|
||||
}
|
||||
|
||||
/* if the signing file exists, set that too */
|
||||
for (guint i = 0; suffixes[i] != NULL; i++) {
|
||||
/* find out if the payload is signed, falling back to detached */
|
||||
item = jcat_file_get_item_by_id (self->jcat_file, basename, NULL);
|
||||
if (item != NULL) {
|
||||
g_autoptr(GError) error_local = NULL;
|
||||
g_autoptr(GPtrArray) results = NULL;
|
||||
results = jcat_context_verify_item (self->jcat_context,
|
||||
blob, item,
|
||||
JCAT_VERIFY_FLAG_REQUIRE_CHECKSUM |
|
||||
JCAT_VERIFY_FLAG_REQUIRE_SIGNATURE,
|
||||
&error_local);
|
||||
if (results == NULL) {
|
||||
g_debug ("failed to verify payload %s: %s",
|
||||
basename, error_local->message);
|
||||
} else {
|
||||
g_debug ("verified payload %s: %u",
|
||||
basename, results->len);
|
||||
release_flags |= FWUPD_RELEASE_FLAG_TRUSTED_PAYLOAD;
|
||||
}
|
||||
|
||||
/* legacy GPG detached signature */
|
||||
} else {
|
||||
g_autofree gchar *basename_sig = NULL;
|
||||
basename_sig = g_strdup_printf ("%s.%s", basename, suffixes[i]);
|
||||
basename_sig = g_strdup_printf ("%s.asc", basename);
|
||||
cabfile = fu_cabinet_get_file_by_name (self, basename_sig);
|
||||
if (cabfile != NULL) {
|
||||
g_autofree gchar *release_key_sig = NULL;
|
||||
blob = gcab_file_get_bytes (cabfile);
|
||||
if (blob == NULL) {
|
||||
GBytes *data_sig;
|
||||
g_autoptr(JcatResult) jcat_result = NULL;
|
||||
g_autoptr(JcatBlob) jcat_blob = NULL;
|
||||
g_autoptr(GError) error_local = NULL;
|
||||
|
||||
data_sig = gcab_file_get_bytes (cabfile);
|
||||
if (data_sig == NULL) {
|
||||
g_set_error (error,
|
||||
FWUPD_ERROR,
|
||||
FWUPD_ERROR_INVALID_FILE,
|
||||
@ -201,12 +255,25 @@ fu_cabinet_parse_release (FuCabinet *self, XbNode *release, GError **error)
|
||||
basename_sig);
|
||||
return FALSE;
|
||||
}
|
||||
release_key_sig = g_strdup_printf ("fwupd::ReleaseBlob(%s)",
|
||||
basename_sig);
|
||||
xb_node_set_data (release, release_key_sig, blob);
|
||||
jcat_blob = jcat_blob_new (JCAT_BLOB_KIND_GPG, data_sig);
|
||||
jcat_result = jcat_context_verify_blob (self->jcat_context,
|
||||
blob, jcat_blob,
|
||||
JCAT_VERIFY_FLAG_REQUIRE_SIGNATURE,
|
||||
&error_local);
|
||||
if (jcat_result == NULL) {
|
||||
g_debug ("failed to verify payload %s using detached: %s",
|
||||
basename, error_local->message);
|
||||
} else {
|
||||
g_debug ("verified payload %s using detached", basename);
|
||||
release_flags |= FWUPD_RELEASE_FLAG_TRUSTED_PAYLOAD;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* this means we can get the data from fu_keyring_get_release_flags */
|
||||
release_flags_blob = g_bytes_new (&release_flags, sizeof(release_flags));
|
||||
xb_node_set_data (release, "fwupd::ReleaseFlags", release_flags_blob);
|
||||
|
||||
/* success */
|
||||
return TRUE;
|
||||
}
|
||||
@ -283,11 +350,22 @@ fu_cabinet_set_container_checksum_cb (XbBuilderFixup *builder_fixup,
|
||||
|
||||
/* adds each GCabFile to the silo */
|
||||
static gboolean
|
||||
fu_cabinet_build_silo_file (FuCabinet *self, GCabFile *cabfile, GError **error)
|
||||
fu_cabinet_build_silo_file (FuCabinet *self,
|
||||
GCabFile *cabfile,
|
||||
FwupdReleaseFlags release_flags,
|
||||
GError **error)
|
||||
{
|
||||
GBytes *blob;
|
||||
g_autoptr(GError) error_local = NULL;
|
||||
g_autoptr(XbBuilderSource) source = xb_builder_source_new ();
|
||||
g_autoptr(XbBuilderNode) bn_info = xb_builder_node_new ("info");
|
||||
|
||||
/* indicate the metainfo file was signed */
|
||||
if (release_flags & FWUPD_RELEASE_FLAG_TRUSTED_METADATA) {
|
||||
g_autoptr(XbBuilderNode) bn_trust = NULL;
|
||||
xb_builder_node_insert (bn_info, "metadata_trust", NULL);
|
||||
}
|
||||
xb_builder_source_set_info (source, bn_info);
|
||||
|
||||
/* rewrite to be under a components root */
|
||||
xb_builder_source_set_prefix (source, "components");
|
||||
@ -318,6 +396,71 @@ fu_cabinet_build_silo_file (FuCabinet *self, GCabFile *cabfile, GError **error)
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
fu_cabinet_build_silo_metainfo (FuCabinet *self, GCabFile *cabfile, GError **error)
|
||||
{
|
||||
FwupdReleaseFlags release_flags = FWUPD_RELEASE_FLAG_NONE;
|
||||
const gchar *fn = gcab_file_get_extract_name (cabfile);
|
||||
g_autoptr(JcatItem) item = NULL;
|
||||
|
||||
/* validate against the Jcat file */
|
||||
item = jcat_file_get_item_by_id (self->jcat_file, fn, NULL);
|
||||
if (item == NULL) {
|
||||
g_debug ("failed to verify %s: no JcatItem", fn);
|
||||
} else {
|
||||
g_autoptr(GError) error_local = NULL;
|
||||
g_autoptr(GPtrArray) results = NULL;
|
||||
results = jcat_context_verify_item (self->jcat_context,
|
||||
gcab_file_get_bytes (cabfile),
|
||||
item,
|
||||
JCAT_VERIFY_FLAG_REQUIRE_CHECKSUM |
|
||||
JCAT_VERIFY_FLAG_REQUIRE_SIGNATURE,
|
||||
&error_local);
|
||||
if (results == NULL) {
|
||||
g_debug ("failed to verify %s: %s",
|
||||
fn, error_local->message);
|
||||
} else {
|
||||
g_debug ("verified metadata %s: %u",
|
||||
fn, results->len);
|
||||
release_flags |= FWUPD_RELEASE_FLAG_TRUSTED_METADATA;
|
||||
}
|
||||
}
|
||||
|
||||
/* actually parse the XML now */
|
||||
g_debug ("processing file: %s", fn);
|
||||
if (!fu_cabinet_build_silo_file (self, cabfile, release_flags, error)) {
|
||||
g_prefix_error (error, "%s could not be loaded: ",
|
||||
gcab_file_get_extract_name (cabfile));
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* success */
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/* load the firmware.jcat files if included */
|
||||
static gboolean
|
||||
fu_cabinet_build_jcat_folder (FuCabinet *self, 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);
|
||||
if (g_str_has_suffix (fn, ".jcat")) {
|
||||
GBytes *data_jcat = gcab_file_get_bytes (cabfile);
|
||||
g_autoptr(GInputStream) istream = NULL;
|
||||
istream = g_memory_input_stream_new_from_bytes (data_jcat);
|
||||
if (!jcat_file_import_stream (self->jcat_file,
|
||||
istream,
|
||||
JCAT_IMPORT_FLAG_NONE,
|
||||
NULL,
|
||||
error))
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/* adds each GCabFolder to the silo */
|
||||
static gboolean
|
||||
fu_cabinet_build_silo_folder (FuCabinet *self, GCabFolder *cabfolder, GError **error)
|
||||
@ -326,14 +469,10 @@ fu_cabinet_build_silo_folder (FuCabinet *self, GCabFolder *cabfolder, GError **e
|
||||
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 (g_str_has_suffix (fn, ".metainfo.xml")) {
|
||||
if (!fu_cabinet_build_silo_file (self, cabfile, error)) {
|
||||
g_prefix_error (error, "%s could not be loaded: ",
|
||||
gcab_file_get_extract_name (cabfile));
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
if (!g_str_has_suffix (fn, ".metainfo.xml"))
|
||||
continue;
|
||||
if (!fu_cabinet_build_silo_metainfo (self, cabfile, error))
|
||||
return FALSE;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
@ -352,8 +491,17 @@ fu_cabinet_build_silo (FuCabinet *self, GBytes *data, GError **error)
|
||||
XB_SILO_PROFILE_FLAG_DEBUG);
|
||||
}
|
||||
|
||||
/* look at each folder */
|
||||
/* load Jcat */
|
||||
folders = gcab_cabinet_get_folders (self->gcab_cabinet);
|
||||
if (self->jcat_context != NULL) {
|
||||
for (guint i = 0; i < folders->len; i++) {
|
||||
GCabFolder *cabfolder = GCAB_FOLDER (g_ptr_array_index (folders, i));
|
||||
if (!fu_cabinet_build_jcat_folder (self, cabfolder, error))
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
/* adds each metainfo file to the silo */
|
||||
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);
|
||||
|
@ -8,6 +8,7 @@
|
||||
|
||||
#include <glib-object.h>
|
||||
#include <xmlb.h>
|
||||
#include <jcat.h>
|
||||
|
||||
#define FU_TYPE_CABINET (fu_cabinet_get_type ())
|
||||
|
||||
@ -28,6 +29,8 @@ typedef enum {
|
||||
FuCabinet *fu_cabinet_new (void);
|
||||
void fu_cabinet_set_size_max (FuCabinet *self,
|
||||
guint64 size_max);
|
||||
void fu_cabinet_set_jcat_context (FuCabinet *self,
|
||||
JcatContext *jcat_context);
|
||||
gboolean fu_cabinet_parse (FuCabinet *self,
|
||||
GBytes *data,
|
||||
FuCabinetParseFlags flags,
|
||||
|
@ -746,9 +746,7 @@ fu_common_store_cab_func (void)
|
||||
csum = xb_node_query_first (rel, "checksum[@target='content']", &error);
|
||||
g_assert_nonnull (csum);
|
||||
g_assert_cmpstr (xb_node_get_text (csum), ==, "7c211433f02071597741e6ff5a8ea34789abbf43");
|
||||
blob_tmp = xb_node_get_data (rel, "fwupd::ReleaseBlob(firmware.dfu)");
|
||||
g_assert_nonnull (blob_tmp);
|
||||
blob_tmp = xb_node_get_data (rel, "fwupd::ReleaseBlob(firmware.dfu.asc)");
|
||||
blob_tmp = xb_node_get_data (rel, "fwupd::FirmwareBlob");
|
||||
g_assert_nonnull (blob_tmp);
|
||||
req = xb_node_query_first (component, "requires/id", &error);
|
||||
g_assert_no_error (error);
|
||||
@ -791,10 +789,8 @@ fu_common_store_cab_unsigned_func (void)
|
||||
g_assert_cmpstr (xb_node_get_attr (rel, "version"), ==, "1.2.3");
|
||||
csum = xb_node_query_first (rel, "checksum[@target='content']", &error);
|
||||
g_assert_null (csum);
|
||||
blob_tmp = xb_node_get_data (rel, "fwupd::ReleaseBlob(firmware.bin)");
|
||||
blob_tmp = xb_node_get_data (rel, "fwupd::FirmwareBlob");
|
||||
g_assert_nonnull (blob_tmp);
|
||||
blob_tmp = xb_node_get_data (rel, "fwupd::ReleaseBlob(firmware.bin.asc)");
|
||||
g_assert_null (blob_tmp);
|
||||
}
|
||||
|
||||
static void
|
||||
@ -830,7 +826,7 @@ fu_common_store_cab_folder_func (void)
|
||||
g_assert_no_error (error);
|
||||
g_assert_nonnull (rel);
|
||||
g_assert_cmpstr (xb_node_get_attr (rel, "version"), ==, "1.2.3");
|
||||
blob_tmp = xb_node_get_data (rel, "fwupd::ReleaseBlob(firmware.bin)");
|
||||
blob_tmp = xb_node_get_data (rel, "fwupd::FirmwareBlob");
|
||||
g_assert_nonnull (blob_tmp);
|
||||
}
|
||||
|
||||
|
@ -544,6 +544,7 @@ LIBFWUPDPLUGIN_1.4.0 {
|
||||
fu_cabinet_get_type;
|
||||
fu_cabinet_new;
|
||||
fu_cabinet_parse;
|
||||
fu_cabinet_set_jcat_context;
|
||||
fu_cabinet_set_size_max;
|
||||
fu_device_get_root;
|
||||
fu_device_set_version_bootloader;
|
||||
|
@ -79,6 +79,7 @@ fwupdplugin_headers_private = [
|
||||
|
||||
introspection_deps = [
|
||||
libxmlb,
|
||||
libjcat,
|
||||
giounix,
|
||||
gusb,
|
||||
]
|
||||
@ -95,6 +96,7 @@ library_deps = [
|
||||
libjsonglib,
|
||||
libgcab,
|
||||
valgrind,
|
||||
libjcat,
|
||||
platform_deps,
|
||||
]
|
||||
|
||||
@ -136,6 +138,7 @@ fwupdplugin_pkgg.generate(
|
||||
'libgcab-1.0',
|
||||
'libsoup-2.4',
|
||||
'xmlb',
|
||||
'jcat',
|
||||
],
|
||||
subdirs : 'fwupd-1',
|
||||
version : meson.project_version(),
|
||||
@ -218,9 +221,6 @@ if get_option('tests')
|
||||
noreqs_test_firmware,
|
||||
fu_hash,
|
||||
]
|
||||
if get_option('pkcs7')
|
||||
test_deps += colorhug_pkcs7_signature
|
||||
endif
|
||||
testdatadir_src = join_paths(meson.source_root(), 'data', 'tests')
|
||||
testdatadir_dst = join_paths(meson.build_root(), 'data', 'tests')
|
||||
pluginbuilddir = join_paths(meson.build_root(), 'plugins', 'test')
|
||||
|
13
meson.build
13
meson.build
@ -187,6 +187,7 @@ else
|
||||
gudev = dependency('', required : false)
|
||||
endif
|
||||
libxmlb = dependency('xmlb', version : '>= 0.1.13', fallback : ['libxmlb', 'libxmlb_dep'])
|
||||
libjcat = dependency('jcat', version : '>= 0.1.0', fallback : ['libjcat', 'libjcat_dep'])
|
||||
gusb = dependency('gusb', version : '>= 0.2.9', fallback : ['gusb', 'gusb_dep'])
|
||||
sqlite = dependency('sqlite3')
|
||||
libarchive = dependency('libarchive')
|
||||
@ -206,18 +207,6 @@ if build_daemon
|
||||
udevdir = udev.get_pkgconfig_variable('udevdir')
|
||||
endif
|
||||
endif
|
||||
if get_option('pkcs7')
|
||||
gnutls = dependency('gnutls', version : '>= 3.4.4.1')
|
||||
if gnutls.version().version_compare('>= 3.6.0')
|
||||
conf.set('HAVE_GNUTLS_3_6_0', '1')
|
||||
endif
|
||||
conf.set('ENABLE_PKCS7', '1')
|
||||
endif
|
||||
if get_option('gpg')
|
||||
gpgme = cc.find_library('gpgme')
|
||||
gpgerror = cc.find_library('gpg-error')
|
||||
conf.set('ENABLE_GPG', '1')
|
||||
endif
|
||||
libm = cc.find_library('m', required: false)
|
||||
libgcab = dependency('libgcab-1.0', version : '>= 1.0', fallback : ['gcab', 'gcab_dep'])
|
||||
gcab = find_program('gcab', required : true)
|
||||
|
@ -2,13 +2,11 @@ option('build', type : 'combo', choices : ['all', 'standalone', 'library'], valu
|
||||
option('agent', type : 'boolean', value : true, description : 'enable the fwupd agent')
|
||||
option('consolekit', type : 'boolean', value : true, description : 'enable ConsoleKit support')
|
||||
option('firmware-packager', type : 'boolean', value : true, description : 'enable firmware-packager installation')
|
||||
option('gpg', type : 'boolean', value : true, description : 'enable the GPG verification support')
|
||||
option('gtkdoc', type : 'boolean', value : false, description : 'enable developer documentation')
|
||||
option('introspection', type : 'boolean', value : true, description : 'generate GObject Introspection data')
|
||||
option('lvfs', type : 'boolean', value : true, description : 'enable LVFS remotes')
|
||||
option('man', type : 'boolean', value : true, description : 'enable man pages')
|
||||
option('gudev', type : 'boolean', value : true, description : 'enable GUdev support')
|
||||
option('pkcs7', type : 'boolean', value : true, description : 'enable the PKCS7 verification support')
|
||||
option('plugin_altos', type : 'boolean', value : true, description : 'enable altos support')
|
||||
option('plugin_amt', type : 'boolean', value : true, description : 'enable Intel AMT support')
|
||||
option('plugin_dell', type : 'boolean', value : true, description : 'enable Dell-specific support')
|
||||
|
@ -45,6 +45,31 @@ parts:
|
||||
- -lib/pkgconfig
|
||||
build-packages:
|
||||
- libssl-dev
|
||||
nettle:
|
||||
plugin: autotools
|
||||
source: https://ftp.gnu.org/gnu/nettle/nettle-3.5.tar.gz
|
||||
build-packages:
|
||||
- libgmp-dev
|
||||
prime:
|
||||
- -include
|
||||
- -bin
|
||||
- -share/man
|
||||
- -lib/pkgconfig
|
||||
gnutls:
|
||||
plugin: autotools
|
||||
source: https://www.gnupg.org/ftp/gcrypt/gnutls/v3.6/gnutls-3.6.12.tar.xz
|
||||
build-packages:
|
||||
- libtasn1-6-dev
|
||||
- libunistring-dev
|
||||
- libidn2-dev
|
||||
- libunbound-dev
|
||||
- libp11-kit-dev
|
||||
prime:
|
||||
- -include
|
||||
- -bin
|
||||
- -share/man
|
||||
- -lib/pkgconfig
|
||||
after: [nettle]
|
||||
meson:
|
||||
plugin: python
|
||||
source: https://github.com/mesonbuild/meson/releases/download/0.51.2/meson-0.51.2.tar.gz
|
||||
@ -150,7 +175,11 @@ parts:
|
||||
-Dplugin_modem_manager=true,
|
||||
-Dudevdir=$SNAPCRAFT_STAGE/lib/udev,
|
||||
"-Dlibxmlb:gtkdoc=false",
|
||||
"-Dlibxmlb:introspection=false"]
|
||||
"-Dlibxmlb:introspection=false",
|
||||
"-Dlibjcat:man=false",
|
||||
"-Dlibjcat:gtkdoc=false",
|
||||
"-Dlibjcat:introspection=false",
|
||||
"-Dlibjcat:tests=false"]
|
||||
source: .
|
||||
source-type: git
|
||||
override-build: |
|
||||
@ -159,7 +188,6 @@ parts:
|
||||
build-packages:
|
||||
- bash-completion
|
||||
- gcab
|
||||
- gnutls-dev
|
||||
- gnu-efi
|
||||
- libarchive-dev
|
||||
- libcairo-dev
|
||||
@ -236,7 +264,9 @@ parts:
|
||||
- -usr/share/gir-1.0
|
||||
- -usr/share/upstart
|
||||
- -usr/lib/*/pkgconfig
|
||||
after: [meson, build-introspection, modemmanager, libmbim, libqmi, tpm2-tss]
|
||||
# we don't want system gnutls leaking in
|
||||
- -usr/lib/*/libgnutls*
|
||||
after: [meson, build-introspection, modemmanager, libmbim, libqmi, tpm2-tss, gnutls]
|
||||
fix-bash-completion:
|
||||
plugin: make
|
||||
source: contrib/snap/fix-bash-completion
|
||||
|
226
src/fu-engine.c
226
src/fu-engine.c
@ -97,6 +97,7 @@ struct _FuEngine
|
||||
GHashTable *approved_firmware;
|
||||
GHashTable *firmware_gtypes;
|
||||
gchar *host_machine_id;
|
||||
JcatContext *jcat_context;
|
||||
gboolean loaded;
|
||||
};
|
||||
|
||||
@ -1728,8 +1729,7 @@ fu_engine_install_release (FuEngine *self,
|
||||
FuPlugin *plugin;
|
||||
FwupdVersionFormat fmt;
|
||||
GBytes *blob_fw;
|
||||
const gchar *tmp = NULL;
|
||||
g_autofree gchar *release_key = NULL;
|
||||
const gchar *tmp;
|
||||
g_autofree gchar *version_orig = NULL;
|
||||
g_autofree gchar *version_rel = NULL;
|
||||
g_autoptr(FuDevice) device_tmp = NULL;
|
||||
@ -1737,19 +1737,13 @@ fu_engine_install_release (FuEngine *self,
|
||||
g_autoptr(GBytes) blob_fw2 = NULL;
|
||||
g_autoptr(GError) error_local = NULL;
|
||||
|
||||
/* get the blob */
|
||||
tmp = xb_node_query_attr (rel, "checksum[@target='content']", "filename", NULL);
|
||||
if (tmp == NULL)
|
||||
tmp = "firmware.bin";
|
||||
|
||||
/* not all devices have to use the same blob */
|
||||
release_key = g_strdup_printf ("fwupd::ReleaseBlob(%s)", tmp);
|
||||
blob_fw = xb_node_get_data (rel, release_key);
|
||||
/* get per-release firmware blob */
|
||||
blob_fw = xb_node_get_data (rel, "fwupd::FirmwareBlob");
|
||||
if (blob_fw == NULL) {
|
||||
g_set_error (error,
|
||||
FWUPD_ERROR,
|
||||
FWUPD_ERROR_READ,
|
||||
"Failed to get firmware blob using %s", tmp);
|
||||
g_set_error_literal (error,
|
||||
FWUPD_ERROR,
|
||||
FWUPD_ERROR_INTERNAL,
|
||||
"Failed to get firmware blob from release");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
@ -2994,22 +2988,82 @@ fu_engine_remote_list_changed_cb (FuRemoteList *remote_list, FuEngine *self)
|
||||
fu_engine_emit_changed (self);
|
||||
}
|
||||
|
||||
static FuKeyringResult *
|
||||
fu_engine_get_existing_keyring_result (FuEngine *self,
|
||||
FuKeyring *kr,
|
||||
FwupdRemote *remote,
|
||||
GError **error)
|
||||
static gint
|
||||
fu_engine_sort_jcat_results_timestamp_cb (gconstpointer a, gconstpointer b)
|
||||
{
|
||||
JcatResult *ra = *((JcatResult **) a);
|
||||
JcatResult *rb = *((JcatResult **) b);
|
||||
if (jcat_result_get_timestamp (ra) < jcat_result_get_timestamp (rb))
|
||||
return -1;
|
||||
if (jcat_result_get_timestamp (ra) > jcat_result_get_timestamp (rb))
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static JcatResult *
|
||||
fu_engine_get_system_jcat_result (FuEngine *self, FwupdRemote *remote, GError **error)
|
||||
{
|
||||
g_autoptr(GBytes) blob = NULL;
|
||||
g_autoptr(GBytes) blob_sig = NULL;
|
||||
g_autoptr(GInputStream) istream = NULL;
|
||||
g_autoptr(GPtrArray) results = NULL;
|
||||
g_autoptr(JcatItem) jcat_item = NULL;
|
||||
g_autoptr(JcatFile) jcat_file = jcat_file_new ();
|
||||
|
||||
blob = fu_common_get_contents_bytes (fwupd_remote_get_filename_cache (remote), error);
|
||||
if (blob == NULL)
|
||||
return NULL;
|
||||
blob_sig = fu_common_get_contents_bytes (fwupd_remote_get_filename_cache_sig (remote), error);
|
||||
if (blob_sig == NULL)
|
||||
return NULL;
|
||||
return fu_keyring_verify_data (kr, blob, blob_sig,
|
||||
FU_KEYRING_VERIFY_FLAG_NONE, error);
|
||||
istream = g_memory_input_stream_new_from_bytes (blob_sig);
|
||||
if (!jcat_file_import_stream (jcat_file, istream,
|
||||
JCAT_IMPORT_FLAG_NONE,
|
||||
NULL, error))
|
||||
return NULL;
|
||||
jcat_item = jcat_file_get_item_default (jcat_file, error);
|
||||
if (jcat_item == NULL)
|
||||
return NULL;
|
||||
results = jcat_context_verify_item (self->jcat_context,
|
||||
blob, jcat_item,
|
||||
JCAT_VERIFY_FLAG_REQUIRE_CHECKSUM |
|
||||
JCAT_VERIFY_FLAG_REQUIRE_SIGNATURE,
|
||||
error);
|
||||
if (results == NULL)
|
||||
return NULL;
|
||||
g_ptr_array_sort (results, fu_engine_sort_jcat_results_timestamp_cb);
|
||||
|
||||
/* return the newest one */
|
||||
return g_object_ref (g_ptr_array_index (results, 0));
|
||||
}
|
||||
|
||||
static gboolean
|
||||
fu_engine_validate_result_timestamp (JcatResult *jcat_result,
|
||||
JcatResult *jcat_result_old,
|
||||
GError **error)
|
||||
{
|
||||
gint64 delta = 0;
|
||||
|
||||
g_return_val_if_fail (JCAT_IS_RESULT (jcat_result), FALSE);
|
||||
g_return_val_if_fail (JCAT_IS_RESULT (jcat_result_old), FALSE);
|
||||
|
||||
if (jcat_result_get_timestamp (jcat_result) > 0 &&
|
||||
jcat_result_get_timestamp (jcat_result_old) > 0) {
|
||||
delta = jcat_result_get_timestamp (jcat_result) -
|
||||
jcat_result_get_timestamp (jcat_result_old);
|
||||
}
|
||||
if (delta < 0) {
|
||||
g_set_error (error,
|
||||
FWUPD_ERROR,
|
||||
FWUPD_ERROR_INVALID_FILE,
|
||||
"new signing timestamp was %"
|
||||
G_GINT64_FORMAT " seconds older",
|
||||
-delta);
|
||||
return FALSE;
|
||||
}
|
||||
if (delta > 0)
|
||||
g_debug ("timestamp increased, so no rollback");
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -3017,7 +3071,7 @@ fu_engine_get_existing_keyring_result (FuEngine *self,
|
||||
* @self: A #FuEngine
|
||||
* @remote_id: A remote ID, e.g. `lvfs`
|
||||
* @bytes_raw: Blob of metadata
|
||||
* @bytes_sig: Blob of metadata signature
|
||||
* @bytes_sig: Blob of metadata signature, typically Jcat binary format
|
||||
* @error: A #GError, or %NULL
|
||||
*
|
||||
* Updates the metadata for a specific remote.
|
||||
@ -3059,31 +3113,41 @@ fu_engine_update_metadata_bytes (FuEngine *self, const gchar *remote_id,
|
||||
/* verify file */
|
||||
keyring_kind = fwupd_remote_get_keyring_kind (remote);
|
||||
if (keyring_kind != FWUPD_KEYRING_KIND_NONE) {
|
||||
g_autoptr(FuKeyring) kr = NULL;
|
||||
g_autoptr(FuKeyringResult) kr_result = NULL;
|
||||
g_autoptr(FuKeyringResult) kr_result_old = NULL;
|
||||
JcatResult *jcat_result;
|
||||
g_autoptr(GError) error_local = NULL;
|
||||
kr = fu_keyring_create_for_kind (keyring_kind, error);
|
||||
if (kr == NULL)
|
||||
g_autoptr(GInputStream) istream = NULL;
|
||||
g_autoptr(GPtrArray) results = NULL;
|
||||
g_autoptr(JcatFile) jcat_file = jcat_file_new ();
|
||||
g_autoptr(JcatItem) jcat_item = NULL;
|
||||
g_autoptr(JcatResult) jcat_result_old = NULL;
|
||||
|
||||
/* load Jcat file */
|
||||
istream = g_memory_input_stream_new_from_bytes (bytes_sig);
|
||||
if (!jcat_file_import_stream (jcat_file, istream,
|
||||
JCAT_IMPORT_FLAG_NONE,
|
||||
NULL, error))
|
||||
return FALSE;
|
||||
if (!fu_keyring_setup (kr, error))
|
||||
|
||||
/* this should only be signing one thing */
|
||||
jcat_item = jcat_file_get_item_default (jcat_file, error);
|
||||
if (jcat_item == NULL)
|
||||
return FALSE;
|
||||
sysconfdir = fu_common_get_path (FU_PATH_KIND_SYSCONFDIR);
|
||||
pki_dir = g_build_filename (sysconfdir, "pki", "fwupd-metadata", NULL);
|
||||
if (!fu_keyring_add_public_keys (kr, pki_dir, error))
|
||||
return FALSE;
|
||||
kr_result = fu_keyring_verify_data (kr, bytes_raw, bytes_sig,
|
||||
FU_KEYRING_VERIFY_FLAG_NONE,
|
||||
results = jcat_context_verify_item (self->jcat_context,
|
||||
bytes_raw, jcat_item,
|
||||
JCAT_VERIFY_FLAG_REQUIRE_CHECKSUM |
|
||||
JCAT_VERIFY_FLAG_REQUIRE_SIGNATURE,
|
||||
error);
|
||||
if (kr_result == NULL)
|
||||
if (results == NULL)
|
||||
return FALSE;
|
||||
|
||||
/* return the newest one */
|
||||
g_ptr_array_sort (results, fu_engine_sort_jcat_results_timestamp_cb);
|
||||
jcat_result = g_ptr_array_index (results, 0);
|
||||
|
||||
/* verify the metadata was signed later than the existing
|
||||
* metadata for this remote to mitigate a rollback attack */
|
||||
kr_result_old = fu_engine_get_existing_keyring_result (self, kr,
|
||||
remote,
|
||||
&error_local);
|
||||
if (kr_result_old == NULL) {
|
||||
jcat_result_old = fu_engine_get_system_jcat_result (self, remote, &error_local);
|
||||
if (jcat_result_old == NULL) {
|
||||
if (g_error_matches (error_local,
|
||||
G_FILE_ERROR,
|
||||
G_FILE_ERROR_NOENT)) {
|
||||
@ -3094,23 +3158,10 @@ fu_engine_update_metadata_bytes (FuEngine *self, const gchar *remote_id,
|
||||
error_local->message);
|
||||
}
|
||||
} else {
|
||||
gint64 delta = 0;
|
||||
if (fu_keyring_result_get_timestamp (kr_result) > 0 &&
|
||||
fu_keyring_result_get_timestamp (kr_result_old) > 0) {
|
||||
delta = fu_keyring_result_get_timestamp (kr_result) -
|
||||
fu_keyring_result_get_timestamp (kr_result_old);
|
||||
}
|
||||
if (delta < 0) {
|
||||
g_set_error (error,
|
||||
FWUPD_ERROR,
|
||||
FWUPD_ERROR_INVALID_FILE,
|
||||
"new signing timestamp was %"
|
||||
G_GINT64_FORMAT " seconds older",
|
||||
-delta);
|
||||
if (!fu_engine_validate_result_timestamp (jcat_result,
|
||||
jcat_result_old,
|
||||
error))
|
||||
return FALSE;
|
||||
} else if (delta > 0) {
|
||||
g_debug ("timestamp increased, so no rollback");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -3210,6 +3261,7 @@ fu_engine_get_silo_from_blob (FuEngine *self, GBytes *blob_cab, GError **error)
|
||||
/* load file */
|
||||
fu_engine_set_status (self, FWUPD_STATUS_DECOMPRESSING);
|
||||
fu_cabinet_set_size_max (cabinet, fu_engine_get_archive_size_max (self));
|
||||
fu_cabinet_set_jcat_context (cabinet, self->jcat_context);
|
||||
if (!fu_cabinet_parse (cabinet, blob_cab, FU_CABINET_PARSE_FLAG_NONE, error))
|
||||
return NULL;
|
||||
silo = fu_cabinet_get_silo (cabinet);
|
||||
@ -3952,31 +4004,30 @@ fu_engine_add_approved_firmware (FuEngine *self, const gchar *checksum)
|
||||
gchar *
|
||||
fu_engine_self_sign (FuEngine *self,
|
||||
const gchar *value,
|
||||
FuKeyringSignFlags flags,
|
||||
JcatSignFlags flags,
|
||||
GError **error)
|
||||
{
|
||||
g_autoptr(FuKeyring) kr = NULL;
|
||||
g_autoptr(FuKeyringResult) kr_result = NULL;
|
||||
g_autoptr(JcatBlob) jcat_signature = NULL;
|
||||
g_autoptr(JcatEngine) jcat_engine = NULL;
|
||||
g_autoptr(JcatResult) jcat_result = NULL;
|
||||
g_autoptr(GBytes) payload = NULL;
|
||||
g_autoptr(GBytes) signature = NULL;
|
||||
|
||||
/* create detached signature and verify */
|
||||
kr = fu_keyring_create_for_kind (FWUPD_KEYRING_KIND_PKCS7, error);
|
||||
if (kr == NULL)
|
||||
return NULL;
|
||||
if (!fu_keyring_setup (kr, error))
|
||||
jcat_engine = jcat_context_get_engine (self->jcat_context,
|
||||
JCAT_BLOB_KIND_PKCS7,
|
||||
error);
|
||||
if (jcat_engine == NULL)
|
||||
return NULL;
|
||||
payload = g_bytes_new (value, strlen (value));
|
||||
signature = fu_keyring_sign_data (kr, payload, flags, error);
|
||||
if (signature == NULL)
|
||||
jcat_signature = jcat_engine_self_sign (jcat_engine, payload, flags, error);
|
||||
if (jcat_signature == NULL)
|
||||
return NULL;
|
||||
kr_result = fu_keyring_verify_data (kr, payload, signature,
|
||||
FU_KEYRING_VERIFY_FLAG_USE_CLIENT_CERT,
|
||||
error);
|
||||
if (kr_result == NULL)
|
||||
jcat_result = jcat_engine_self_verify (jcat_engine, payload,
|
||||
jcat_blob_get_data (jcat_signature),
|
||||
JCAT_VERIFY_FLAG_NONE, error);
|
||||
if (jcat_result == NULL)
|
||||
return NULL;
|
||||
return g_strndup (g_bytes_get_data (signature, NULL),
|
||||
g_bytes_get_size (signature));
|
||||
return jcat_blob_get_data_as_string (jcat_signature);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -5219,23 +5270,21 @@ fu_engine_udev_uevent_cb (GUdevClient *gudev_client,
|
||||
static void
|
||||
fu_engine_ensure_client_certificate (FuEngine *self)
|
||||
{
|
||||
g_autoptr(FuKeyring) kr = NULL;
|
||||
g_autoptr(GBytes) blob = g_bytes_new_static ("test\0", 5);
|
||||
g_autoptr(GBytes) sig = NULL;
|
||||
g_autoptr(GError) error = NULL;
|
||||
g_autoptr(JcatBlob) jcat_sig = NULL;
|
||||
g_autoptr(JcatEngine) jcat_engine = NULL;
|
||||
|
||||
/* create keyring and sign dummy data to ensure certificate exists */
|
||||
kr = fu_keyring_create_for_kind (FWUPD_KEYRING_KIND_PKCS7, &error);
|
||||
if (kr == NULL) {
|
||||
jcat_engine = jcat_context_get_engine (self->jcat_context,
|
||||
JCAT_BLOB_KIND_PKCS7,
|
||||
&error);
|
||||
if (jcat_engine == NULL) {
|
||||
g_message ("failed to create keyring: %s", error->message);
|
||||
return;
|
||||
}
|
||||
if (!fu_keyring_setup (kr, &error)) {
|
||||
g_message ("failed to setup keyring: %s", error->message);
|
||||
return;
|
||||
}
|
||||
sig = fu_keyring_sign_data (kr, blob, FU_KEYRING_SIGN_FLAG_NONE, &error);
|
||||
if (sig == NULL) {
|
||||
jcat_sig = jcat_engine_self_sign (jcat_engine, blob, JCAT_SIGN_FLAG_NONE, &error);
|
||||
if (jcat_sig == NULL) {
|
||||
g_message ("failed to sign using keyring: %s", error->message);
|
||||
return;
|
||||
}
|
||||
@ -5486,6 +5535,10 @@ fu_engine_init (FuEngine *self)
|
||||
#ifdef HAVE_UTSNAME_H
|
||||
struct utsname uname_tmp;
|
||||
#endif
|
||||
g_autofree gchar *keyring_path = NULL;
|
||||
g_autofree gchar *pkidir_fw = NULL;
|
||||
g_autofree gchar *pkidir_md = NULL;
|
||||
g_autofree gchar *sysconfdir = NULL;
|
||||
self->percentage = 0;
|
||||
self->status = FWUPD_STATUS_IDLE;
|
||||
self->config = fu_config_new ();
|
||||
@ -5518,6 +5571,16 @@ fu_engine_init (FuEngine *self)
|
||||
g_signal_connect (self->idle, "notify::status",
|
||||
G_CALLBACK (fu_engine_idle_status_notify_cb), self);
|
||||
|
||||
/* setup Jcat context */
|
||||
self->jcat_context = jcat_context_new ();
|
||||
keyring_path = fu_common_get_path (FU_PATH_KIND_LOCALSTATEDIR_PKG);
|
||||
jcat_context_set_keyring_path (self->jcat_context, keyring_path);
|
||||
sysconfdir = fu_common_get_path (FU_PATH_KIND_SYSCONFDIR);
|
||||
pkidir_fw = g_build_filename (sysconfdir, "pki", "fwupd", NULL);
|
||||
jcat_context_add_public_keys (self->jcat_context, pkidir_fw);
|
||||
pkidir_md = g_build_filename (sysconfdir, "pki", "fwupd-metadata", NULL);
|
||||
jcat_context_add_public_keys (self->jcat_context, pkidir_md);
|
||||
|
||||
/* add some runtime versions of things the daemon depends on */
|
||||
fu_engine_add_runtime_version (self, "org.freedesktop.fwupd", VERSION);
|
||||
fu_engine_add_runtime_version (self, "com.redhat.fwupdate", "12");
|
||||
@ -5573,6 +5636,7 @@ fu_engine_finalize (GObject *obj)
|
||||
g_object_unref (self->hwids);
|
||||
g_object_unref (self->history);
|
||||
g_object_unref (self->device_list);
|
||||
g_object_unref (self->jcat_context);
|
||||
g_ptr_array_unref (self->plugin_filter);
|
||||
g_ptr_array_unref (self->udev_subsystems);
|
||||
#ifdef HAVE_GUDEV
|
||||
|
@ -8,12 +8,12 @@
|
||||
|
||||
#include <xmlb.h>
|
||||
#include <glib-object.h>
|
||||
#include <jcat.h>
|
||||
|
||||
#include "fwupd-device.h"
|
||||
#include "fwupd-enums.h"
|
||||
|
||||
#include "fu-common.h"
|
||||
#include "fu-keyring.h"
|
||||
#include "fu-install-task.h"
|
||||
#include "fu-plugin.h"
|
||||
|
||||
@ -147,7 +147,7 @@ void fu_engine_add_approved_firmware (FuEngine *self,
|
||||
const gchar *checksum);
|
||||
gchar *fu_engine_self_sign (FuEngine *self,
|
||||
const gchar *value,
|
||||
FuKeyringSignFlags flags,
|
||||
JcatSignFlags flags,
|
||||
GError **error);
|
||||
gboolean fu_engine_modify_config (FuEngine *self,
|
||||
const gchar *key,
|
||||
|
@ -1,352 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2017 Richard Hughes <richard@hughsie.com>
|
||||
*
|
||||
* SPDX-License-Identifier: LGPL-2.1+
|
||||
*/
|
||||
|
||||
#define G_LOG_DOMAIN "FuKeyring"
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <gpgme.h>
|
||||
|
||||
#include "fwupd-error.h"
|
||||
|
||||
#include "fu-common.h"
|
||||
#include "fu-keyring-gpg.h"
|
||||
|
||||
struct _FuKeyringGpg
|
||||
{
|
||||
FuKeyring parent_instance;
|
||||
gpgme_ctx_t ctx;
|
||||
};
|
||||
|
||||
G_DEFINE_TYPE (FuKeyringGpg, fu_keyring_gpg, FU_TYPE_KEYRING)
|
||||
|
||||
G_DEFINE_AUTO_CLEANUP_FREE_FUNC(gpgme_data_t, gpgme_data_release, NULL)
|
||||
|
||||
static gboolean
|
||||
fu_keyring_gpg_add_public_key (FuKeyringGpg *self,
|
||||
const gchar *filename,
|
||||
GError **error)
|
||||
{
|
||||
gpgme_error_t rc;
|
||||
gpgme_import_result_t result;
|
||||
gpgme_import_status_t s;
|
||||
g_auto(gpgme_data_t) data = NULL;
|
||||
|
||||
/* import public key */
|
||||
g_debug ("Adding GnuPG public key %s", filename);
|
||||
rc = gpgme_data_new_from_file (&data, filename, 1);
|
||||
if (rc != GPG_ERR_NO_ERROR) {
|
||||
g_set_error (error,
|
||||
FWUPD_ERROR,
|
||||
FWUPD_ERROR_INTERNAL,
|
||||
"failed to load %s: %s",
|
||||
filename, gpgme_strerror (rc));
|
||||
return FALSE;
|
||||
}
|
||||
rc = gpgme_op_import (self->ctx, data);
|
||||
if (rc != GPG_ERR_NO_ERROR) {
|
||||
g_set_error (error,
|
||||
FWUPD_ERROR,
|
||||
FWUPD_ERROR_INTERNAL,
|
||||
"failed to import %s: %s",
|
||||
filename, gpgme_strerror (rc));
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* print what keys were imported */
|
||||
result = gpgme_op_import_result (self->ctx);
|
||||
for (s = result->imports; s != NULL; s = s->next) {
|
||||
g_debug ("importing key %s [%u] %s",
|
||||
s->fpr, s->status, gpgme_strerror (s->result));
|
||||
}
|
||||
|
||||
/* make sure keys were really imported */
|
||||
if (result->imported == 0 && result->unchanged == 0) {
|
||||
g_debug("imported: %d, unchanged: %d, not_imported: %d",
|
||||
result->imported,
|
||||
result->unchanged,
|
||||
result->not_imported);
|
||||
g_set_error (error,
|
||||
FWUPD_ERROR,
|
||||
FWUPD_ERROR_INTERNAL,
|
||||
"key import failed %s",
|
||||
filename);
|
||||
return FALSE;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
fu_keyring_gpg_setup (FuKeyring *keyring, GError **error)
|
||||
{
|
||||
FuKeyringGpg *self = FU_KEYRING_GPG (keyring);
|
||||
gpgme_error_t rc;
|
||||
g_autofree gchar *gpg_home = NULL;
|
||||
g_autofree gchar *localstatedir = NULL;
|
||||
|
||||
if (self->ctx != NULL)
|
||||
return TRUE;
|
||||
|
||||
/* startup gpgme */
|
||||
rc = gpg_err_init ();
|
||||
if (rc != GPG_ERR_NO_ERROR) {
|
||||
g_set_error (error,
|
||||
FWUPD_ERROR,
|
||||
FWUPD_ERROR_INTERNAL,
|
||||
"failed to init: %s",
|
||||
gpgme_strerror (rc));
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* create a new GPG context */
|
||||
rc = gpgme_new (&self->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 (self->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;
|
||||
}
|
||||
|
||||
/* set a custom home directory */
|
||||
localstatedir = fu_common_get_path (FU_PATH_KIND_LOCALSTATEDIR_PKG);
|
||||
gpg_home = g_build_filename (localstatedir, "gnupg", NULL);
|
||||
if (g_mkdir_with_parents (gpg_home, 0700) < 0) {
|
||||
g_set_error (error,
|
||||
FWUPD_ERROR,
|
||||
FWUPD_ERROR_INTERNAL,
|
||||
"failed to create %s",
|
||||
gpg_home);
|
||||
return FALSE;
|
||||
}
|
||||
g_debug ("Using keyring at %s", gpg_home);
|
||||
rc = gpgme_ctx_set_engine_info (self->ctx,
|
||||
GPGME_PROTOCOL_OpenPGP,
|
||||
NULL,
|
||||
gpg_home);
|
||||
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 (self->ctx, TRUE);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
fu_keyring_gpg_add_public_keys (FuKeyring *keyring,
|
||||
const gchar *path,
|
||||
GError **error)
|
||||
{
|
||||
FuKeyringGpg *self = FU_KEYRING_GPG (keyring);
|
||||
const gchar *fn_tmp;
|
||||
g_autoptr(GDir) dir = NULL;
|
||||
|
||||
/* search all the public key files */
|
||||
dir = g_dir_open (path, 0, error);
|
||||
if (dir == NULL)
|
||||
return FALSE;
|
||||
while ((fn_tmp = g_dir_read_name (dir)) != NULL) {
|
||||
g_autofree gchar *path_tmp = NULL;
|
||||
if (!g_str_has_prefix (fn_tmp, "GPG-KEY-"))
|
||||
continue;
|
||||
path_tmp = g_build_filename (path, fn_tmp, NULL);
|
||||
if (!fu_keyring_gpg_add_public_key (self, path_tmp, error))
|
||||
return FALSE;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
fu_keyring_gpg_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;
|
||||
}
|
||||
|
||||
static FuKeyringResult *
|
||||
fu_keyring_gpg_verify_data (FuKeyring *keyring,
|
||||
GBytes *blob,
|
||||
GBytes *blob_signature,
|
||||
FuKeyringVerifyFlags flags,
|
||||
GError **error)
|
||||
{
|
||||
FuKeyringGpg *self = FU_KEYRING_GPG (keyring);
|
||||
gpgme_error_t rc;
|
||||
gpgme_signature_t s;
|
||||
gpgme_verify_result_t result;
|
||||
gint64 timestamp_newest = 0;
|
||||
g_auto(gpgme_data_t) data = NULL;
|
||||
g_auto(gpgme_data_t) sig = NULL;
|
||||
g_autoptr(GString) authority_newest = g_string_new (NULL);
|
||||
|
||||
/* not supported */
|
||||
if (flags & FU_KEYRING_VERIFY_FLAG_USE_CLIENT_CERT) {
|
||||
g_set_error_literal (error,
|
||||
FWUPD_ERROR,
|
||||
FWUPD_ERROR_NOT_SUPPORTED,
|
||||
"no GPG client certificate support");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* load file data */
|
||||
rc = gpgme_data_new_from_mem (&data,
|
||||
g_bytes_get_data (blob, NULL),
|
||||
g_bytes_get_size (blob), 0);
|
||||
if (rc != GPG_ERR_NO_ERROR) {
|
||||
g_set_error (error,
|
||||
FWUPD_ERROR,
|
||||
FWUPD_ERROR_INTERNAL,
|
||||
"failed to load data: %s",
|
||||
gpgme_strerror (rc));
|
||||
return NULL;
|
||||
}
|
||||
rc = gpgme_data_new_from_mem (&sig,
|
||||
g_bytes_get_data (blob_signature, NULL),
|
||||
g_bytes_get_size (blob_signature), 0);
|
||||
if (rc != GPG_ERR_NO_ERROR) {
|
||||
g_set_error (error,
|
||||
FWUPD_ERROR,
|
||||
FWUPD_ERROR_INTERNAL,
|
||||
"failed to load signature: %s",
|
||||
gpgme_strerror (rc));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* verify */
|
||||
rc = gpgme_op_verify (self->ctx, sig, data, NULL);
|
||||
if (rc != GPG_ERR_NO_ERROR) {
|
||||
g_set_error (error,
|
||||
FWUPD_ERROR,
|
||||
FWUPD_ERROR_INTERNAL,
|
||||
"failed to verify data: %s",
|
||||
gpgme_strerror (rc));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
/* verify the result */
|
||||
result = gpgme_op_verify_result (self->ctx);
|
||||
if (result == NULL) {
|
||||
g_set_error_literal (error,
|
||||
FWUPD_ERROR,
|
||||
FWUPD_ERROR_INTERNAL,
|
||||
"no result record from libgpgme");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* look at each signature */
|
||||
for (s = result->signatures; s != NULL ; s = s->next ) {
|
||||
g_debug ("returned signature fingerprint %s", s->fpr);
|
||||
if (!fu_keyring_gpg_check_signature (s, error))
|
||||
return NULL;
|
||||
|
||||
/* save details about the key for the result */
|
||||
if ((gint64) s->timestamp > timestamp_newest) {
|
||||
timestamp_newest = (gint64) s->timestamp;
|
||||
g_string_assign (authority_newest, s->fpr);
|
||||
}
|
||||
}
|
||||
return FU_KEYRING_RESULT (g_object_new (FU_TYPE_KEYRING_RESULT,
|
||||
"timestamp", timestamp_newest,
|
||||
"authority", authority_newest->str,
|
||||
NULL));
|
||||
}
|
||||
|
||||
static void
|
||||
fu_keyring_gpg_finalize (GObject *object)
|
||||
{
|
||||
FuKeyringGpg *self = FU_KEYRING_GPG (object);
|
||||
if (self->ctx != NULL)
|
||||
gpgme_release (self->ctx);
|
||||
G_OBJECT_CLASS (fu_keyring_gpg_parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
static void
|
||||
fu_keyring_gpg_class_init (FuKeyringGpgClass *klass)
|
||||
{
|
||||
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
||||
FuKeyringClass *klass_app = FU_KEYRING_CLASS (klass);
|
||||
klass_app->setup = fu_keyring_gpg_setup;
|
||||
klass_app->add_public_keys = fu_keyring_gpg_add_public_keys;
|
||||
klass_app->verify_data = fu_keyring_gpg_verify_data;
|
||||
object_class->finalize = fu_keyring_gpg_finalize;
|
||||
}
|
||||
|
||||
static void
|
||||
fu_keyring_gpg_init (FuKeyringGpg *self)
|
||||
{
|
||||
FuKeyring *keyring = FU_KEYRING (self);
|
||||
g_autofree gchar *name = NULL;
|
||||
name = g_strdup_printf ("gpgme-v%s", gpgme_check_version (NULL));
|
||||
fu_keyring_set_name (keyring, name);
|
||||
}
|
||||
|
||||
FuKeyring *
|
||||
fu_keyring_gpg_new (void)
|
||||
{
|
||||
return FU_KEYRING (g_object_new (FU_TYPE_KEYRING_GPG, NULL));
|
||||
}
|
@ -1,15 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2017 Richard Hughes <richard@hughsie.com>
|
||||
*
|
||||
* SPDX-License-Identifier: LGPL-2.1+
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "fu-keyring.h"
|
||||
|
||||
#define FU_TYPE_KEYRING_GPG (fu_keyring_gpg_get_type ())
|
||||
|
||||
G_DECLARE_FINAL_TYPE (FuKeyringGpg, fu_keyring_gpg, FU, KEYRING_GPG, FuKeyring)
|
||||
|
||||
FuKeyring *fu_keyring_gpg_new (void);
|
@ -1,836 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2017 Richard Hughes <richard@hughsie.com>
|
||||
*
|
||||
* SPDX-License-Identifier: LGPL-2.1+
|
||||
*/
|
||||
|
||||
#define G_LOG_DOMAIN "FuKeyring"
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <gnutls/abstract.h>
|
||||
#include <gnutls/crypto.h>
|
||||
#include <gnutls/pkcs7.h>
|
||||
|
||||
#include "fu-common.h"
|
||||
#include "fu-keyring-pkcs7.h"
|
||||
|
||||
#include "fwupd-error.h"
|
||||
|
||||
struct _FuKeyringPkcs7
|
||||
{
|
||||
FuKeyring parent_instance;
|
||||
gnutls_x509_trust_list_t tl;
|
||||
};
|
||||
|
||||
G_DEFINE_TYPE (FuKeyringPkcs7, fu_keyring_pkcs7, FU_TYPE_KEYRING)
|
||||
|
||||
typedef guchar gnutls_data_t;
|
||||
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wunused-function"
|
||||
G_DEFINE_AUTO_CLEANUP_FREE_FUNC(gnutls_pkcs7_t, gnutls_pkcs7_deinit, NULL)
|
||||
G_DEFINE_AUTO_CLEANUP_FREE_FUNC(gnutls_privkey_t, gnutls_privkey_deinit, NULL)
|
||||
G_DEFINE_AUTO_CLEANUP_FREE_FUNC(gnutls_pubkey_t, gnutls_pubkey_deinit, NULL)
|
||||
G_DEFINE_AUTO_CLEANUP_FREE_FUNC(gnutls_x509_crt_t, gnutls_x509_crt_deinit, NULL)
|
||||
G_DEFINE_AUTO_CLEANUP_FREE_FUNC(gnutls_x509_dn_t, gnutls_x509_dn_deinit, NULL)
|
||||
G_DEFINE_AUTO_CLEANUP_FREE_FUNC(gnutls_x509_privkey_t, gnutls_x509_privkey_deinit, NULL)
|
||||
#ifdef HAVE_GNUTLS_3_6_0
|
||||
G_DEFINE_AUTO_CLEANUP_FREE_FUNC(gnutls_x509_spki_t, gnutls_x509_spki_deinit, NULL)
|
||||
#endif
|
||||
G_DEFINE_AUTOPTR_CLEANUP_FUNC(gnutls_data_t, gnutls_free)
|
||||
#pragma clang diagnostic pop
|
||||
|
||||
static gnutls_x509_crt_t
|
||||
fu_keyring_pkcs7_load_crt_from_filename (const gchar *filename,
|
||||
gnutls_x509_crt_fmt_t format,
|
||||
GError **error)
|
||||
{
|
||||
gnutls_datum_t d = { 0 };
|
||||
gsize bufsz = 0;
|
||||
int rc;
|
||||
g_autofree gchar *buf = NULL;
|
||||
g_auto(gnutls_x509_crt_t) crt = NULL;
|
||||
|
||||
/* create certificate */
|
||||
rc = gnutls_x509_crt_init (&crt);
|
||||
if (rc < 0) {
|
||||
g_set_error (error,
|
||||
FWUPD_ERROR,
|
||||
FWUPD_ERROR_SIGNATURE_INVALID,
|
||||
"crt_init: %s [%i]",
|
||||
gnutls_strerror (rc), rc);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* import the certificate */
|
||||
if (!g_file_get_contents (filename, &buf, &bufsz, error))
|
||||
return NULL;
|
||||
d.size = bufsz;
|
||||
d.data = (unsigned char *) buf;
|
||||
rc = gnutls_x509_crt_import (crt, &d, GNUTLS_X509_FMT_PEM);
|
||||
if (rc < 0) {
|
||||
g_set_error (error,
|
||||
FWUPD_ERROR,
|
||||
FWUPD_ERROR_SIGNATURE_INVALID,
|
||||
"crt_import: %s [%i]",
|
||||
gnutls_strerror (rc), rc);
|
||||
return NULL;
|
||||
}
|
||||
return g_steal_pointer (&crt);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
fu_keyring_pkcs7_add_public_key (FuKeyringPkcs7 *self,
|
||||
const gchar *filename,
|
||||
gnutls_x509_crt_fmt_t format,
|
||||
GError **error)
|
||||
{
|
||||
guint key_usage = 0;
|
||||
int rc;
|
||||
g_auto(gnutls_x509_crt_t) crt = NULL;
|
||||
|
||||
/* load file and add to the trust list */
|
||||
g_debug ("trying to load certificate from %s", filename);
|
||||
crt = fu_keyring_pkcs7_load_crt_from_filename (filename, format, error);
|
||||
if (crt == NULL)
|
||||
return FALSE;
|
||||
rc = gnutls_x509_crt_get_key_usage (crt, &key_usage, NULL);
|
||||
if (rc < 0) {
|
||||
g_set_error (error,
|
||||
FWUPD_ERROR,
|
||||
FWUPD_ERROR_SIGNATURE_INVALID,
|
||||
"failed to get key usage: %s [%i]",
|
||||
gnutls_strerror (rc), rc);
|
||||
return FALSE;
|
||||
}
|
||||
if ((key_usage & GNUTLS_KEY_DIGITAL_SIGNATURE) == 0 &&
|
||||
(key_usage & GNUTLS_KEY_KEY_CERT_SIGN) == 0) {
|
||||
g_set_error (error,
|
||||
FWUPD_ERROR,
|
||||
FWUPD_ERROR_SIGNATURE_INVALID,
|
||||
"certificate %s not suitable for use [0x%x]",
|
||||
filename, key_usage);
|
||||
return FALSE;
|
||||
}
|
||||
rc = gnutls_x509_trust_list_add_cas (self->tl, &crt, 1, 0);
|
||||
if (rc < 0) {
|
||||
g_set_error (error,
|
||||
FWUPD_ERROR,
|
||||
FWUPD_ERROR_SIGNATURE_INVALID,
|
||||
"failed to add to trust list: %s [%i]",
|
||||
gnutls_strerror (rc), rc);
|
||||
return FALSE;
|
||||
}
|
||||
g_debug ("loaded %i certificates", rc);
|
||||
|
||||
/* confusingly the trust list does not copy the certificate */
|
||||
crt = NULL;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
fu_keyring_pkcs7_add_public_keys (FuKeyring *keyring,
|
||||
const gchar *path,
|
||||
GError **error)
|
||||
{
|
||||
FuKeyringPkcs7 *self = FU_KEYRING_PKCS7 (keyring);
|
||||
const gchar *fn_tmp;
|
||||
g_autoptr(GDir) dir = NULL;
|
||||
|
||||
/* search all the public key files */
|
||||
dir = g_dir_open (path, 0, error);
|
||||
if (dir == NULL)
|
||||
return FALSE;
|
||||
while ((fn_tmp = g_dir_read_name (dir)) != NULL) {
|
||||
g_autofree gchar *path_tmp = NULL;
|
||||
path_tmp = g_build_filename (path, fn_tmp, NULL);
|
||||
if (g_str_has_suffix (fn_tmp, ".pem")) {
|
||||
if (!fu_keyring_pkcs7_add_public_key (self, path_tmp,
|
||||
GNUTLS_X509_FMT_PEM,
|
||||
error))
|
||||
return FALSE;
|
||||
}
|
||||
if (g_str_has_suffix (fn_tmp, ".cer") ||
|
||||
g_str_has_suffix (fn_tmp, ".crt") ||
|
||||
g_str_has_suffix (fn_tmp, ".der")) {
|
||||
if (!fu_keyring_pkcs7_add_public_key (self, path_tmp,
|
||||
GNUTLS_X509_FMT_DER,
|
||||
error))
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gnutls_privkey_t
|
||||
fu_keyring_pkcs7_load_privkey (FuKeyringPkcs7 *self, GError **error)
|
||||
{
|
||||
int rc;
|
||||
gnutls_datum_t d = { 0 };
|
||||
gsize bufsz = 0;
|
||||
g_autofree gchar *buf = NULL;
|
||||
g_autofree gchar *fn = NULL;
|
||||
g_autofree gchar *localstatedir = NULL;
|
||||
g_auto(gnutls_privkey_t) key = NULL;
|
||||
|
||||
/* load the private key */
|
||||
rc = gnutls_privkey_init (&key);
|
||||
if (rc < 0) {
|
||||
g_set_error (error,
|
||||
FWUPD_ERROR,
|
||||
FWUPD_ERROR_SIGNATURE_INVALID,
|
||||
"privkey_init: %s [%i]",
|
||||
gnutls_strerror (rc), rc);
|
||||
return NULL;
|
||||
}
|
||||
localstatedir = fu_common_get_path (FU_PATH_KIND_LOCALSTATEDIR_PKG);
|
||||
fn = g_build_filename (localstatedir, "pki", "secret.key", NULL);
|
||||
if (!g_file_get_contents (fn, &buf, &bufsz, error))
|
||||
return NULL;
|
||||
d.size = bufsz;
|
||||
d.data = (unsigned char *) buf;
|
||||
rc = gnutls_privkey_import_x509_raw (key, &d, GNUTLS_X509_FMT_PEM, NULL, 0);
|
||||
if (rc < 0) {
|
||||
g_set_error (error,
|
||||
FWUPD_ERROR,
|
||||
FWUPD_ERROR_SIGNATURE_INVALID,
|
||||
"privkey_import_x509_raw: %s [%i]",
|
||||
gnutls_strerror (rc), rc);
|
||||
return NULL;
|
||||
}
|
||||
return g_steal_pointer (&key);
|
||||
}
|
||||
|
||||
static gnutls_x509_crt_t
|
||||
fu_keyring_pkcs7_load_client_certificate (FuKeyringPkcs7 *self, GError **error)
|
||||
{
|
||||
g_autofree gchar *filename = NULL;
|
||||
g_autofree gchar *localstatedir = NULL;
|
||||
localstatedir = fu_common_get_path (FU_PATH_KIND_LOCALSTATEDIR_PKG);
|
||||
filename = g_build_filename (localstatedir, "pki", "client.pem", NULL);
|
||||
return fu_keyring_pkcs7_load_crt_from_filename (filename,
|
||||
GNUTLS_X509_FMT_PEM,
|
||||
error);
|
||||
}
|
||||
|
||||
static gnutls_pubkey_t
|
||||
fu_keyring_pkcs7_load_pubkey_from_privkey (gnutls_privkey_t privkey, GError **error)
|
||||
{
|
||||
g_auto(gnutls_pubkey_t) pubkey = NULL;
|
||||
int rc;
|
||||
|
||||
/* get the public key part of the private key */
|
||||
rc = gnutls_pubkey_init (&pubkey);
|
||||
if (rc < 0) {
|
||||
g_set_error (error,
|
||||
FWUPD_ERROR,
|
||||
FWUPD_ERROR_SIGNATURE_INVALID,
|
||||
"pubkey_init: %s [%i]",
|
||||
gnutls_strerror (rc), rc);
|
||||
return NULL;
|
||||
}
|
||||
rc = gnutls_pubkey_import_privkey (pubkey, privkey, 0, 0);
|
||||
if (rc < 0) {
|
||||
g_set_error (error,
|
||||
FWUPD_ERROR,
|
||||
FWUPD_ERROR_SIGNATURE_INVALID,
|
||||
"pubkey_import_privkey: %s [%i]",
|
||||
gnutls_strerror (rc), rc);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* success */
|
||||
return g_steal_pointer (&pubkey);
|
||||
}
|
||||
|
||||
/* generates a private key just like:
|
||||
* `certtool --generate-privkey` */
|
||||
static gboolean
|
||||
fu_keyring_pkcs7_ensure_private_key (FuKeyringPkcs7 *self, GError **error)
|
||||
{
|
||||
#ifdef HAVE_GNUTLS_3_6_0
|
||||
gnutls_datum_t d = { 0 };
|
||||
int bits;
|
||||
int key_type = GNUTLS_PK_RSA;
|
||||
int rc;
|
||||
g_autofree gchar *fn = NULL;
|
||||
g_autofree gchar *localstatedir = NULL;
|
||||
g_auto(gnutls_x509_privkey_t) key = NULL;
|
||||
g_auto(gnutls_x509_spki_t) spki = NULL;
|
||||
g_autoptr(GFile) file = NULL;
|
||||
g_autoptr(gnutls_data_t) d_payload = NULL;
|
||||
|
||||
/* check exists */
|
||||
localstatedir = fu_common_get_path (FU_PATH_KIND_LOCALSTATEDIR_PKG);
|
||||
fn = g_build_filename (localstatedir, "pki", "secret.key", NULL);
|
||||
if (g_file_test (fn, G_FILE_TEST_EXISTS))
|
||||
return TRUE;
|
||||
|
||||
/* initialize key and SPKI */
|
||||
rc = gnutls_x509_privkey_init (&key);
|
||||
if (rc < 0) {
|
||||
g_set_error (error,
|
||||
FWUPD_ERROR,
|
||||
FWUPD_ERROR_SIGNATURE_INVALID,
|
||||
"privkey_init: %s [%i]",
|
||||
gnutls_strerror (rc), rc);
|
||||
return FALSE;
|
||||
}
|
||||
rc = gnutls_x509_spki_init (&spki);
|
||||
if (rc < 0) {
|
||||
g_set_error (error,
|
||||
FWUPD_ERROR,
|
||||
FWUPD_ERROR_SIGNATURE_INVALID,
|
||||
"spki_init: %s [%i]",
|
||||
gnutls_strerror (rc), rc);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* generate key */
|
||||
bits = gnutls_sec_param_to_pk_bits (key_type, GNUTLS_SEC_PARAM_HIGH);
|
||||
g_debug ("generating a %d bit %s private key...",
|
||||
bits, gnutls_pk_algorithm_get_name (key_type));
|
||||
rc = gnutls_x509_privkey_generate2(key, key_type, bits, 0, NULL, 0);
|
||||
if (rc < 0) {
|
||||
g_set_error (error,
|
||||
FWUPD_ERROR,
|
||||
FWUPD_ERROR_SIGNATURE_INVALID,
|
||||
"privkey_generate2: %s [%i]",
|
||||
gnutls_strerror (rc), rc);
|
||||
return FALSE;
|
||||
}
|
||||
rc = gnutls_x509_privkey_verify_params (key);
|
||||
if (rc < 0) {
|
||||
g_set_error (error,
|
||||
FWUPD_ERROR,
|
||||
FWUPD_ERROR_SIGNATURE_INVALID,
|
||||
"privkey_verify_params: %s [%i]",
|
||||
gnutls_strerror (rc), rc);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* create parents if required */
|
||||
if (!fu_common_mkdir_parent (fn, error))
|
||||
return FALSE;
|
||||
|
||||
/* save to file */
|
||||
rc = gnutls_x509_privkey_export2 (key, GNUTLS_X509_FMT_PEM, &d);
|
||||
if (rc < 0) {
|
||||
g_set_error (error,
|
||||
FWUPD_ERROR,
|
||||
FWUPD_ERROR_SIGNATURE_INVALID,
|
||||
"privkey_export2: %s [%i]",
|
||||
gnutls_strerror (rc), rc);
|
||||
return FALSE;
|
||||
}
|
||||
d_payload = d.data;
|
||||
file = g_file_new_for_path (fn);
|
||||
return g_file_replace_contents (file, (const char *) d_payload, d.size,
|
||||
NULL, FALSE, G_FILE_CREATE_PRIVATE, NULL,
|
||||
NULL, error);
|
||||
#else
|
||||
g_set_error (error,
|
||||
FWUPD_ERROR,
|
||||
FWUPD_ERROR_NOT_SUPPORTED,
|
||||
"cannot build private key as GnuTLS version is too old");
|
||||
return FALSE;
|
||||
#endif
|
||||
}
|
||||
|
||||
/* generates a self signed certificate just like:
|
||||
* `certtool --generate-self-signed --load-privkey priv.pem` */
|
||||
static gboolean
|
||||
fu_keyring_pkcs7_ensure_client_certificate (FuKeyringPkcs7 *self, GError **error)
|
||||
{
|
||||
int rc;
|
||||
gnutls_datum_t d = { 0 };
|
||||
guchar sha1buf[20];
|
||||
gsize sha1bufsz = sizeof(sha1buf);
|
||||
g_autofree gchar *fn = NULL;
|
||||
g_autofree gchar *localstatedir = NULL;
|
||||
g_auto(gnutls_privkey_t) key = NULL;
|
||||
g_auto(gnutls_pubkey_t) pubkey = NULL;
|
||||
g_auto(gnutls_x509_crt_t) crt = NULL;
|
||||
g_autoptr(gnutls_data_t) d_payload = NULL;
|
||||
|
||||
/* check exists */
|
||||
localstatedir = fu_common_get_path (FU_PATH_KIND_LOCALSTATEDIR_PKG);
|
||||
fn = g_build_filename (localstatedir, "pki", "client.pem", NULL);
|
||||
if (g_file_test (fn, G_FILE_TEST_EXISTS))
|
||||
return TRUE;
|
||||
|
||||
/* ensure the private key exists */
|
||||
if (!fu_keyring_pkcs7_ensure_private_key (self, error)) {
|
||||
g_prefix_error (error, "failed to generate private key: ");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* load private key */
|
||||
key = fu_keyring_pkcs7_load_privkey (self, error);
|
||||
if (key == NULL)
|
||||
return FALSE;
|
||||
|
||||
/* load the public key from the private key */
|
||||
pubkey = fu_keyring_pkcs7_load_pubkey_from_privkey (key, error);
|
||||
if (pubkey == NULL)
|
||||
return FALSE;
|
||||
|
||||
/* create certificate */
|
||||
rc = gnutls_x509_crt_init (&crt);
|
||||
if (rc < 0) {
|
||||
g_set_error (error,
|
||||
FWUPD_ERROR,
|
||||
FWUPD_ERROR_SIGNATURE_INVALID,
|
||||
"crt_init: %s [%i]",
|
||||
gnutls_strerror (rc), rc);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* set public key */
|
||||
rc = gnutls_x509_crt_set_pubkey (crt, pubkey);
|
||||
if (rc < 0) {
|
||||
g_set_error (error,
|
||||
FWUPD_ERROR,
|
||||
FWUPD_ERROR_SIGNATURE_INVALID,
|
||||
"crt_set_pubkey: %s [%i]",
|
||||
gnutls_strerror (rc), rc);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* set positive random serial number */
|
||||
rc = gnutls_rnd (GNUTLS_RND_NONCE, sha1buf, sizeof(sha1buf));
|
||||
if (rc < 0) {
|
||||
g_set_error (error,
|
||||
FWUPD_ERROR,
|
||||
FWUPD_ERROR_SIGNATURE_INVALID,
|
||||
"gnutls_rnd: %s [%i]",
|
||||
gnutls_strerror (rc), rc);
|
||||
return FALSE;
|
||||
}
|
||||
sha1buf[0] &= 0x7f;
|
||||
rc = gnutls_x509_crt_set_serial(crt, sha1buf, sizeof(sha1buf));
|
||||
if (rc < 0) {
|
||||
g_set_error (error,
|
||||
FWUPD_ERROR,
|
||||
FWUPD_ERROR_SIGNATURE_INVALID,
|
||||
"crt_set_serial: %s [%i]",
|
||||
gnutls_strerror (rc), rc);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* set activation */
|
||||
rc = gnutls_x509_crt_set_activation_time (crt, time (NULL));
|
||||
if (rc < 0) {
|
||||
g_set_error (error,
|
||||
FWUPD_ERROR,
|
||||
FWUPD_ERROR_SIGNATURE_INVALID,
|
||||
"set_activation_time: %s [%i]",
|
||||
gnutls_strerror (rc), rc);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* set expiration */
|
||||
rc = gnutls_x509_crt_set_expiration_time (crt, (time_t) -1);
|
||||
if (rc < 0) {
|
||||
g_set_error (error,
|
||||
FWUPD_ERROR,
|
||||
FWUPD_ERROR_SIGNATURE_INVALID,
|
||||
"set_expiration_time: %s [%i]",
|
||||
gnutls_strerror (rc), rc);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* set basic constraints */
|
||||
rc = gnutls_x509_crt_set_basic_constraints (crt, 0, -1);
|
||||
if (rc < 0) {
|
||||
g_set_error (error,
|
||||
FWUPD_ERROR,
|
||||
FWUPD_ERROR_SIGNATURE_INVALID,
|
||||
"set_basic_constraints: %s [%i]",
|
||||
gnutls_strerror (rc), rc);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* set usage */
|
||||
rc = gnutls_x509_crt_set_key_usage (crt, GNUTLS_KEY_DIGITAL_SIGNATURE);
|
||||
if (rc < 0) {
|
||||
g_set_error (error,
|
||||
FWUPD_ERROR,
|
||||
FWUPD_ERROR_SIGNATURE_INVALID,
|
||||
"set_key_usage: %s [%i]",
|
||||
gnutls_strerror (rc), rc);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* set subject key ID */
|
||||
rc = gnutls_x509_crt_get_key_id (crt, GNUTLS_KEYID_USE_SHA1, sha1buf, &sha1bufsz);
|
||||
if (rc < 0) {
|
||||
g_set_error (error,
|
||||
FWUPD_ERROR,
|
||||
FWUPD_ERROR_SIGNATURE_INVALID,
|
||||
"get_key_id: %s [%i]",
|
||||
gnutls_strerror (rc), rc);
|
||||
return FALSE;
|
||||
}
|
||||
rc = gnutls_x509_crt_set_subject_key_id (crt, sha1buf, sha1bufsz);
|
||||
if (rc < 0) {
|
||||
g_set_error (error,
|
||||
FWUPD_ERROR,
|
||||
FWUPD_ERROR_SIGNATURE_INVALID,
|
||||
"set_subject_key_id: %s [%i]",
|
||||
gnutls_strerror (rc), rc);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* set version */
|
||||
rc = gnutls_x509_crt_set_version (crt, 3);
|
||||
if (rc < 0) {
|
||||
g_set_error (error,
|
||||
FWUPD_ERROR,
|
||||
FWUPD_ERROR_SIGNATURE_INVALID,
|
||||
"error setting certificate version: %s [%i]",
|
||||
gnutls_strerror (rc), rc);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* self-sign certificate */
|
||||
rc = gnutls_x509_crt_privkey_sign (crt, crt, key, GNUTLS_DIG_SHA256, 0);
|
||||
if (rc < 0) {
|
||||
g_set_error (error,
|
||||
FWUPD_ERROR,
|
||||
FWUPD_ERROR_SIGNATURE_INVALID,
|
||||
"crt_privkey_sign: %s [%i]",
|
||||
gnutls_strerror (rc), rc);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* export to file */
|
||||
rc = gnutls_x509_crt_export2 (crt, GNUTLS_X509_FMT_PEM, &d);
|
||||
if (rc < 0) {
|
||||
g_set_error (error,
|
||||
FWUPD_ERROR,
|
||||
FWUPD_ERROR_SIGNATURE_INVALID,
|
||||
"crt_export2: %s [%i]",
|
||||
gnutls_strerror (rc), rc);
|
||||
return FALSE;
|
||||
}
|
||||
d_payload = d.data;
|
||||
return g_file_set_contents (fn, (const gchar *) d_payload, d.size, error);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
fu_keyring_pkcs7_setup (FuKeyring *keyring, GError **error)
|
||||
{
|
||||
FuKeyringPkcs7 *self = FU_KEYRING_PKCS7 (keyring);
|
||||
int rc;
|
||||
|
||||
if (self->tl != NULL)
|
||||
return TRUE;
|
||||
|
||||
/* create trust list, a bit like a keyring */
|
||||
rc = gnutls_x509_trust_list_init (&self->tl, 0);
|
||||
if (rc != GNUTLS_E_SUCCESS) {
|
||||
g_set_error (error,
|
||||
FWUPD_ERROR,
|
||||
FWUPD_ERROR_SIGNATURE_INVALID,
|
||||
"failed to create trust list: %s [%i]",
|
||||
gnutls_strerror (rc), rc);
|
||||
return FALSE;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
_gnutls_datum_deinit (gnutls_datum_t *d)
|
||||
{
|
||||
gnutls_free (d->data);
|
||||
gnutls_free (d);
|
||||
}
|
||||
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wunused-function"
|
||||
G_DEFINE_AUTOPTR_CLEANUP_FUNC(gnutls_datum_t, _gnutls_datum_deinit)
|
||||
#pragma clang diagnostic pop
|
||||
|
||||
static gchar *
|
||||
fu_keyring_pkcs7_datum_to_dn_str (const gnutls_datum_t *raw)
|
||||
{
|
||||
g_auto(gnutls_x509_dn_t) dn = NULL;
|
||||
g_autoptr(gnutls_datum_t) str = NULL;
|
||||
int rc;
|
||||
rc = gnutls_x509_dn_init (&dn);
|
||||
if (rc < 0)
|
||||
return NULL;
|
||||
rc = gnutls_x509_dn_import (dn, raw);
|
||||
if (rc < 0)
|
||||
return NULL;
|
||||
str = (gnutls_datum_t *) gnutls_malloc (sizeof (gnutls_datum_t));
|
||||
str->data = NULL;
|
||||
rc = gnutls_x509_dn_get_str2 (dn, str, 0);
|
||||
if (rc < 0)
|
||||
return NULL;
|
||||
return g_strndup ((const gchar *) str->data, str->size);
|
||||
}
|
||||
|
||||
/* verifies a detached signature just like:
|
||||
* `certtool --p7-verify --load-certificate client.pem --infile=test.p7b` */
|
||||
static FuKeyringResult *
|
||||
fu_keyring_pkcs7_verify_data (FuKeyring *keyring,
|
||||
GBytes *blob,
|
||||
GBytes *blob_signature,
|
||||
FuKeyringVerifyFlags flags,
|
||||
GError **error)
|
||||
{
|
||||
FuKeyringPkcs7 *self = FU_KEYRING_PKCS7 (keyring);
|
||||
gnutls_datum_t datum = { 0 };
|
||||
gint64 timestamp_newest = 0;
|
||||
int count;
|
||||
int rc;
|
||||
g_auto(gnutls_pkcs7_t) pkcs7 = NULL;
|
||||
g_auto(gnutls_x509_crt_t) crt = NULL;
|
||||
g_autoptr(GString) authority_newest = g_string_new (NULL);
|
||||
|
||||
/* startup */
|
||||
rc = gnutls_pkcs7_init (&pkcs7);
|
||||
if (rc != GNUTLS_E_SUCCESS) {
|
||||
g_set_error (error,
|
||||
FWUPD_ERROR,
|
||||
FWUPD_ERROR_SIGNATURE_INVALID,
|
||||
"failed to init pkcs7: %s [%i]",
|
||||
gnutls_strerror (rc), rc);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* import the signature */
|
||||
datum.data = (guchar *) g_bytes_get_data (blob_signature, NULL);
|
||||
datum.size = g_bytes_get_size (blob_signature);
|
||||
rc = gnutls_pkcs7_import (pkcs7, &datum, GNUTLS_X509_FMT_PEM);
|
||||
if (rc != GNUTLS_E_SUCCESS) {
|
||||
g_set_error (error,
|
||||
FWUPD_ERROR,
|
||||
FWUPD_ERROR_SIGNATURE_INVALID,
|
||||
"failed to import the PKCS7 signature: %s [%i]",
|
||||
gnutls_strerror (rc), rc);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* verify the blob */
|
||||
datum.data = (guchar *) g_bytes_get_data (blob, NULL);
|
||||
datum.size = g_bytes_get_size (blob);
|
||||
count = gnutls_pkcs7_get_signature_count (pkcs7);
|
||||
g_debug ("got %i PKCS7 signatures", count);
|
||||
if (count == 0) {
|
||||
g_set_error_literal (error,
|
||||
FWUPD_ERROR,
|
||||
FWUPD_ERROR_SIGNATURE_INVALID,
|
||||
"no PKCS7 signatures found");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* use client certificate */
|
||||
if (flags & FU_KEYRING_VERIFY_FLAG_USE_CLIENT_CERT) {
|
||||
if (!fu_keyring_pkcs7_ensure_client_certificate (self, error)) {
|
||||
g_prefix_error (error, "failed to generate client certificate: ");
|
||||
return NULL;
|
||||
}
|
||||
crt = fu_keyring_pkcs7_load_client_certificate (self, error);
|
||||
if (crt == NULL)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
for (gint i = 0; i < count; i++) {
|
||||
gnutls_pkcs7_signature_info_st info;
|
||||
gint64 signing_time = 0;
|
||||
gnutls_certificate_verify_flags verify_flags = 0;
|
||||
|
||||
/* use with care */
|
||||
if (flags & FU_KEYRING_VERIFY_FLAG_DISABLE_TIME_CHECKS) {
|
||||
g_debug ("WARNING: disabling time checks");
|
||||
verify_flags |= GNUTLS_VERIFY_DISABLE_TIME_CHECKS;
|
||||
verify_flags |= GNUTLS_VERIFY_DISABLE_TRUSTED_TIME_CHECKS;
|
||||
}
|
||||
|
||||
/* verify the data against the detached signature */
|
||||
if (flags & FU_KEYRING_VERIFY_FLAG_USE_CLIENT_CERT) {
|
||||
rc = gnutls_pkcs7_verify_direct (pkcs7, crt, i, &datum, 0);
|
||||
} else {
|
||||
rc = gnutls_pkcs7_verify (pkcs7, self->tl,
|
||||
NULL, /* vdata */
|
||||
0, /* vdata_size */
|
||||
i, /* index */
|
||||
&datum, /* data */
|
||||
verify_flags);
|
||||
}
|
||||
if (rc < 0) {
|
||||
g_set_error (error,
|
||||
FWUPD_ERROR,
|
||||
FWUPD_ERROR_SIGNATURE_INVALID,
|
||||
"failed to verify data: %s [%i]",
|
||||
gnutls_strerror (rc), rc);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* save details about the key for the result */
|
||||
rc = gnutls_pkcs7_get_signature_info (pkcs7, i, &info);
|
||||
if (rc < 0) {
|
||||
g_set_error (error,
|
||||
FWUPD_ERROR,
|
||||
FWUPD_ERROR_SIGNATURE_INVALID,
|
||||
"failed to get signature info: %s [%i]",
|
||||
gnutls_strerror (rc), rc);
|
||||
return NULL;
|
||||
}
|
||||
signing_time = info.signing_time > 0 ? (gint64) info.signing_time : 1;
|
||||
if (signing_time > timestamp_newest) {
|
||||
g_autofree gchar *dn = NULL;
|
||||
timestamp_newest = signing_time;
|
||||
dn = fu_keyring_pkcs7_datum_to_dn_str (&info.issuer_dn);
|
||||
if (dn != NULL)
|
||||
g_string_assign (authority_newest, dn);
|
||||
}
|
||||
gnutls_pkcs7_signature_info_deinit (&info);
|
||||
}
|
||||
|
||||
/* success */
|
||||
return FU_KEYRING_RESULT (g_object_new (FU_TYPE_KEYRING_RESULT,
|
||||
"timestamp", timestamp_newest,
|
||||
"authority", authority_newest->str,
|
||||
NULL));
|
||||
}
|
||||
|
||||
/* creates a detached signature just like:
|
||||
* `certtool --p7-detached-sign --load-certificate client.pem \
|
||||
* --load-privkey secret.pem --outfile=test.p7b` */
|
||||
static GBytes *
|
||||
fu_keyring_pkcs7_sign_data (FuKeyring *keyring,
|
||||
GBytes *blob,
|
||||
FuKeyringSignFlags flags,
|
||||
GError **error)
|
||||
{
|
||||
FuKeyringPkcs7 *self = FU_KEYRING_PKCS7 (keyring);
|
||||
gnutls_datum_t d = { 0 };
|
||||
gnutls_digest_algorithm_t dig = GNUTLS_DIG_NULL;
|
||||
guint gnutls_flags = 0;
|
||||
int rc;
|
||||
g_auto(gnutls_pkcs7_t) pkcs7 = NULL;
|
||||
g_auto(gnutls_privkey_t) key = NULL;
|
||||
g_auto(gnutls_pubkey_t) pubkey = NULL;
|
||||
g_auto(gnutls_x509_crt_t) crt = NULL;
|
||||
g_autoptr(gnutls_data_t) d_payload = NULL;
|
||||
|
||||
/* ensure the client certificate exists */
|
||||
if (!fu_keyring_pkcs7_ensure_client_certificate (self, error)) {
|
||||
g_prefix_error (error, "failed to generate client certificate: ");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* import the keys */
|
||||
crt = fu_keyring_pkcs7_load_client_certificate (self, error);
|
||||
if (crt == NULL)
|
||||
return NULL;
|
||||
key = fu_keyring_pkcs7_load_privkey (self, error);
|
||||
if (key == NULL)
|
||||
return NULL;
|
||||
|
||||
/* get the digest algorithm from the publix key */
|
||||
pubkey = fu_keyring_pkcs7_load_pubkey_from_privkey (key, error);
|
||||
if (pubkey == NULL)
|
||||
return NULL;
|
||||
rc = gnutls_pubkey_get_preferred_hash_algorithm (pubkey, &dig, NULL);
|
||||
if (rc < 0) {
|
||||
g_set_error (error,
|
||||
FWUPD_ERROR,
|
||||
FWUPD_ERROR_SIGNATURE_INVALID,
|
||||
"preferred_hash_algorithm: %s [%i]",
|
||||
gnutls_strerror (rc), rc);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* create container */
|
||||
rc = gnutls_pkcs7_init (&pkcs7);
|
||||
if (rc < 0) {
|
||||
g_set_error (error,
|
||||
FWUPD_ERROR,
|
||||
FWUPD_ERROR_SIGNATURE_INVALID,
|
||||
"pkcs7_init: %s [%i]",
|
||||
gnutls_strerror (rc), rc);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* sign data */
|
||||
d.data = (unsigned char *) g_bytes_get_data (blob, NULL);
|
||||
d.size = g_bytes_get_size (blob);
|
||||
if (flags & FU_KEYRING_SIGN_FLAG_ADD_TIMESTAMP)
|
||||
gnutls_flags |= GNUTLS_PKCS7_INCLUDE_TIME;
|
||||
if (flags & FU_KEYRING_SIGN_FLAG_ADD_CERT)
|
||||
gnutls_flags |= GNUTLS_PKCS7_INCLUDE_CERT;
|
||||
rc = gnutls_pkcs7_sign (pkcs7, crt, key, &d, NULL, NULL, dig, gnutls_flags);
|
||||
if (rc < 0) {
|
||||
g_set_error (error,
|
||||
FWUPD_ERROR,
|
||||
FWUPD_ERROR_SIGNATURE_INVALID,
|
||||
"pkcs7_sign: %s [%i]",
|
||||
gnutls_strerror (rc), rc);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* set certificate */
|
||||
if (flags & FU_KEYRING_SIGN_FLAG_ADD_CERT) {
|
||||
rc = gnutls_pkcs7_set_crt (pkcs7, crt);
|
||||
if (rc < 0) {
|
||||
g_set_error (error,
|
||||
FWUPD_ERROR,
|
||||
FWUPD_ERROR_SIGNATURE_INVALID,
|
||||
"pkcs7_set_cr: %s", gnutls_strerror (rc));
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/* export */
|
||||
rc = gnutls_pkcs7_export2 (pkcs7, GNUTLS_X509_FMT_PEM, &d);
|
||||
if (rc < 0) {
|
||||
g_set_error (error,
|
||||
FWUPD_ERROR,
|
||||
FWUPD_ERROR_SIGNATURE_INVALID,
|
||||
"pkcs7_export: %s", gnutls_strerror (rc));
|
||||
return NULL;
|
||||
}
|
||||
d_payload = d.data;
|
||||
return g_bytes_new (d_payload, d.size);
|
||||
}
|
||||
|
||||
static void
|
||||
fu_keyring_pkcs7_finalize (GObject *object)
|
||||
{
|
||||
FuKeyringPkcs7 *self = FU_KEYRING_PKCS7 (object);
|
||||
gnutls_x509_trust_list_deinit (self->tl, 1);
|
||||
G_OBJECT_CLASS (fu_keyring_pkcs7_parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
static void
|
||||
fu_keyring_pkcs7_class_init (FuKeyringPkcs7Class *klass)
|
||||
{
|
||||
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
||||
FuKeyringClass *klass_app = FU_KEYRING_CLASS (klass);
|
||||
klass_app->setup = fu_keyring_pkcs7_setup;
|
||||
klass_app->add_public_keys = fu_keyring_pkcs7_add_public_keys;
|
||||
klass_app->sign_data = fu_keyring_pkcs7_sign_data;
|
||||
klass_app->verify_data = fu_keyring_pkcs7_verify_data;
|
||||
object_class->finalize = fu_keyring_pkcs7_finalize;
|
||||
}
|
||||
|
||||
static void
|
||||
fu_keyring_pkcs7_init (FuKeyringPkcs7 *self)
|
||||
{
|
||||
FuKeyring *keyring = FU_KEYRING (self);
|
||||
g_autofree gchar *name = NULL;
|
||||
name = g_strdup_printf ("gnutls-v%s", gnutls_check_version (NULL));
|
||||
fu_keyring_set_name (keyring, name);
|
||||
}
|
||||
|
||||
FuKeyring *
|
||||
fu_keyring_pkcs7_new (void)
|
||||
{
|
||||
return FU_KEYRING (g_object_new (FU_TYPE_KEYRING_PKCS7, NULL));
|
||||
}
|
@ -1,15 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2017 Richard Hughes <richard@hughsie.com>
|
||||
*
|
||||
* SPDX-License-Identifier: LGPL-2.1+
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "fu-keyring.h"
|
||||
|
||||
#define FU_TYPE_KEYRING_PKCS7 (fu_keyring_pkcs7_get_type ())
|
||||
|
||||
G_DECLARE_FINAL_TYPE (FuKeyringPkcs7, fu_keyring_pkcs7, FU, KEYRING_PKCS7, FuKeyring)
|
||||
|
||||
FuKeyring *fu_keyring_pkcs7_new (void);
|
@ -1,116 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2017 Richard Hughes <richard@hughsie.com>
|
||||
*
|
||||
* SPDX-License-Identifier: LGPL-2.1+
|
||||
*/
|
||||
|
||||
#define G_LOG_DOMAIN "FuKeyring"
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "fwupd-error.h"
|
||||
|
||||
#include "fu-keyring-result.h"
|
||||
|
||||
struct _FuKeyringResult
|
||||
{
|
||||
GObject parent_instance;
|
||||
gint64 timestamp;
|
||||
gchar *authority;
|
||||
};
|
||||
|
||||
G_DEFINE_TYPE (FuKeyringResult, fu_keyring_result, G_TYPE_OBJECT)
|
||||
|
||||
enum {
|
||||
PROP_0,
|
||||
PROP_TIMESTAMP,
|
||||
PROP_AUTHORITY,
|
||||
PROP_LAST
|
||||
};
|
||||
|
||||
gint64
|
||||
fu_keyring_result_get_timestamp (FuKeyringResult *self)
|
||||
{
|
||||
g_return_val_if_fail (FU_IS_KEYRING_RESULT (self), 0);
|
||||
return self->timestamp;
|
||||
}
|
||||
|
||||
const gchar *
|
||||
fu_keyring_result_get_authority (FuKeyringResult *self)
|
||||
{
|
||||
g_return_val_if_fail (FU_IS_KEYRING_RESULT (self), NULL);
|
||||
return self->authority;
|
||||
}
|
||||
|
||||
static void
|
||||
fu_keyring_result_finalize (GObject *object)
|
||||
{
|
||||
FuKeyringResult *self = FU_KEYRING_RESULT (object);
|
||||
g_free (self->authority);
|
||||
G_OBJECT_CLASS (fu_keyring_result_parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
static void
|
||||
fu_keyring_result_get_property (GObject *object, guint prop_id,
|
||||
GValue *value, GParamSpec *pspec)
|
||||
{
|
||||
FuKeyringResult *self = FU_KEYRING_RESULT (object);
|
||||
switch (prop_id) {
|
||||
case PROP_TIMESTAMP:
|
||||
g_value_set_int64 (value, self->timestamp);
|
||||
break;
|
||||
case PROP_AUTHORITY:
|
||||
g_value_set_string (value, self->authority);
|
||||
break;
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
fu_keyring_result_set_property (GObject *object, guint prop_id,
|
||||
const GValue *value, GParamSpec *pspec)
|
||||
{
|
||||
FuKeyringResult *self = FU_KEYRING_RESULT (object);
|
||||
switch (prop_id) {
|
||||
case PROP_TIMESTAMP:
|
||||
self->timestamp = g_value_get_int64 (value);
|
||||
break;
|
||||
case PROP_AUTHORITY:
|
||||
self->authority = g_value_dup_string (value);
|
||||
break;
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
fu_keyring_result_class_init (FuKeyringResultClass *klass)
|
||||
{
|
||||
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
||||
GParamSpec *pspec;
|
||||
|
||||
object_class->get_property = fu_keyring_result_get_property;
|
||||
object_class->set_property = fu_keyring_result_set_property;
|
||||
object_class->finalize = fu_keyring_result_finalize;
|
||||
|
||||
pspec = g_param_spec_int64 ("timestamp", NULL, NULL,
|
||||
0, G_MAXINT64, 0,
|
||||
G_PARAM_READWRITE |
|
||||
G_PARAM_CONSTRUCT_ONLY |
|
||||
G_PARAM_STATIC_NAME);
|
||||
g_object_class_install_property (object_class, PROP_TIMESTAMP, pspec);
|
||||
|
||||
pspec = g_param_spec_string ("authority", NULL, NULL, NULL,
|
||||
G_PARAM_READWRITE |
|
||||
G_PARAM_CONSTRUCT_ONLY |
|
||||
G_PARAM_STATIC_NAME);
|
||||
g_object_class_install_property (object_class, PROP_AUTHORITY, pspec);
|
||||
}
|
||||
|
||||
static void
|
||||
fu_keyring_result_init (FuKeyringResult *self)
|
||||
{
|
||||
}
|
@ -1,16 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2017 Richard Hughes <richard@hughsie.com>
|
||||
*
|
||||
* SPDX-License-Identifier: LGPL-2.1+
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <glib-object.h>
|
||||
|
||||
#define FU_TYPE_KEYRING_RESULT (fu_keyring_result_get_type ())
|
||||
|
||||
G_DECLARE_FINAL_TYPE (FuKeyringResult, fu_keyring_result, FU, KEYRING_RESULT, GObject)
|
||||
|
||||
gint64 fu_keyring_result_get_timestamp (FuKeyringResult *self);
|
||||
const gchar *fu_keyring_result_get_authority (FuKeyringResult *self);
|
@ -1,70 +1,15 @@
|
||||
/*
|
||||
* Copyright (C) 2017-2018 Richard Hughes <richard@hughsie.com>
|
||||
* Copyright (C) 2017-2020 Richard Hughes <richard@hughsie.com>
|
||||
*
|
||||
* SPDX-License-Identifier: LGPL-2.1+
|
||||
*/
|
||||
|
||||
#define G_LOG_DOMAIN "FuKeyring"
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#include "fwupd-error.h"
|
||||
|
||||
#include "fu-common.h"
|
||||
#include "fu-keyring-utils.h"
|
||||
|
||||
#ifdef ENABLE_GPG
|
||||
#include "fu-keyring-gpg.h"
|
||||
#endif
|
||||
#ifdef ENABLE_PKCS7
|
||||
#include "fu-keyring-pkcs7.h"
|
||||
#endif
|
||||
|
||||
/**
|
||||
* fu_keyring_create_for_kind:
|
||||
* @kind: A #FwupdKeyringKind, e.g. %FWUPD_KEYRING_KIND_GPG
|
||||
* @error: A #GError, or %NULL
|
||||
*
|
||||
* Creates a new keyring of the specified kind.
|
||||
*
|
||||
* If the keyring cannot be created (for example, if fwupd is compiled without
|
||||
* GPG support) then an error is returned.
|
||||
*
|
||||
* Returns: (transfer full): a new #FuKeyring, or %NULL for error
|
||||
**/
|
||||
FuKeyring *
|
||||
fu_keyring_create_for_kind (FwupdKeyringKind kind, GError **error)
|
||||
{
|
||||
if (kind == FWUPD_KEYRING_KIND_GPG) {
|
||||
#ifdef ENABLE_GPG
|
||||
return fu_keyring_gpg_new ();
|
||||
#else
|
||||
g_set_error_literal (error,
|
||||
FWUPD_ERROR,
|
||||
FWUPD_ERROR_NOT_SUPPORTED,
|
||||
"Not compiled with GPG support");
|
||||
return NULL;
|
||||
#endif
|
||||
}
|
||||
if (kind == FWUPD_KEYRING_KIND_PKCS7) {
|
||||
#ifdef ENABLE_PKCS7
|
||||
return fu_keyring_pkcs7_new ();
|
||||
#else
|
||||
g_set_error_literal (error,
|
||||
FWUPD_ERROR,
|
||||
FWUPD_ERROR_NOT_SUPPORTED,
|
||||
"Not compiled with PKCS7 support");
|
||||
return NULL;
|
||||
#endif
|
||||
}
|
||||
g_set_error (error,
|
||||
FWUPD_ERROR,
|
||||
FWUPD_ERROR_NOT_SUPPORTED,
|
||||
"Keyring kind %s not supported",
|
||||
fwupd_keyring_kind_to_string (kind));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* fu_keyring_get_release_flags:
|
||||
* @release: A #XbNode, e.g. %FWUPD_KEYRING_KIND_GPG
|
||||
@ -80,97 +25,27 @@ fu_keyring_get_release_flags (XbNode *release,
|
||||
FwupdReleaseFlags *flags,
|
||||
GError **error)
|
||||
{
|
||||
FwupdKeyringKind keyring_kind = FWUPD_KEYRING_KIND_UNKNOWN;
|
||||
GBytes *blob_payload;
|
||||
GBytes *blob_signature;
|
||||
const gchar *fn;
|
||||
g_autofree gchar *pki_dir = NULL;
|
||||
g_autofree gchar *release_key = NULL;
|
||||
g_autofree gchar *sysconfdir = NULL;
|
||||
g_autoptr(GError) error_local = NULL;
|
||||
g_autoptr(FuKeyring) kr = NULL;
|
||||
g_autoptr(FuKeyringResult) kr_result = NULL;
|
||||
struct {
|
||||
FwupdKeyringKind kind;
|
||||
const gchar *ext;
|
||||
} keyrings[] = {
|
||||
{ FWUPD_KEYRING_KIND_GPG, "asc" },
|
||||
{ FWUPD_KEYRING_KIND_PKCS7, "p7b" },
|
||||
{ FWUPD_KEYRING_KIND_PKCS7, "p7c" },
|
||||
{ FWUPD_KEYRING_KIND_NONE, NULL }
|
||||
};
|
||||
GBytes *blob;
|
||||
|
||||
/* custom filename specified */
|
||||
fn = xb_node_query_attr (release, "checksum[@target='content']", "filename", NULL);
|
||||
if (fn == NULL)
|
||||
fn = "filename.bin";
|
||||
|
||||
/* no signature == no trust */
|
||||
for (guint i = 0; keyrings[i].ext != NULL; i++) {
|
||||
g_autofree gchar *fn_tmp = NULL;
|
||||
fn_tmp = g_strdup_printf ("fwupd::ReleaseBlob(%s.%s)",
|
||||
fn, keyrings[i].ext);
|
||||
blob_signature = g_object_get_data (G_OBJECT (release), fn_tmp);
|
||||
if (blob_signature != NULL) {
|
||||
keyring_kind = keyrings[i].kind;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (keyring_kind == FWUPD_KEYRING_KIND_UNKNOWN) {
|
||||
g_debug ("firmware archive contained no signature");
|
||||
blob = g_object_get_data (G_OBJECT (release), "fwupd::ReleaseFlags");
|
||||
if (blob == NULL) {
|
||||
g_debug ("no fwupd::ReleaseFlags set by loader");
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/* get payload */
|
||||
release_key = g_strdup_printf ("fwupd::ReleaseBlob(%s)", fn);
|
||||
blob_payload = g_object_get_data (G_OBJECT (release), release_key);
|
||||
if (blob_payload == NULL) {
|
||||
g_set_error_literal (error,
|
||||
FWUPD_ERROR,
|
||||
FWUPD_ERROR_INVALID_FILE,
|
||||
"no payload");
|
||||
"no fwupd::ReleaseFlags set by loader");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* check we were installed correctly */
|
||||
sysconfdir = fu_common_get_path (FU_PATH_KIND_SYSCONFDIR);
|
||||
pki_dir = g_build_filename (sysconfdir, "pki", PACKAGE_NAME, NULL);
|
||||
#if defined(ENABLE_PKCS7) || defined(ENABLE_PKCS7)
|
||||
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;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* verify against the system trusted keys */
|
||||
kr = fu_keyring_create_for_kind (keyring_kind, error);
|
||||
if (kr == NULL)
|
||||
return FALSE;
|
||||
if (!fu_keyring_setup (kr, error)) {
|
||||
g_prefix_error (error, "failed to set up %s keyring: ",
|
||||
fu_keyring_get_name (kr));
|
||||
return FALSE;
|
||||
}
|
||||
if (!fu_keyring_add_public_keys (kr, pki_dir, error)) {
|
||||
g_prefix_error (error, "failed to add public keys to %s keyring: ",
|
||||
fu_keyring_get_name (kr));
|
||||
return FALSE;
|
||||
}
|
||||
kr_result = fu_keyring_verify_data (kr, blob_payload, blob_signature,
|
||||
FU_KEYRING_VERIFY_FLAG_NONE,
|
||||
&error_local);
|
||||
if (kr_result == NULL) {
|
||||
g_warning ("untrusted as failed to verify from %s keyring: %s",
|
||||
fu_keyring_get_name (kr),
|
||||
error_local->message);
|
||||
if (g_bytes_get_size (blob) != sizeof(FwupdReleaseFlags)) {
|
||||
g_critical ("invalid fwupd::ReleaseFlags set by loader");
|
||||
return TRUE;
|
||||
g_set_error_literal (error,
|
||||
FWUPD_ERROR,
|
||||
FWUPD_ERROR_INVALID_FILE,
|
||||
"invalid fwupd::ReleaseFlags set by loader");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* awesome! */
|
||||
g_debug ("marking payload as trusted");
|
||||
*flags |= FWUPD_RELEASE_FLAG_TRUSTED_PAYLOAD;
|
||||
memcpy (flags, g_bytes_get_data (blob, NULL), sizeof(FwupdReleaseFlags));
|
||||
return TRUE;
|
||||
}
|
||||
|
@ -7,12 +7,10 @@
|
||||
#pragma once
|
||||
|
||||
#include <xmlb.h>
|
||||
#include <jcat.h>
|
||||
|
||||
#include "fu-keyring.h"
|
||||
#include "fwupd-enums.h"
|
||||
|
||||
FuKeyring *fu_keyring_create_for_kind (FwupdKeyringKind kind,
|
||||
GError **error);
|
||||
gboolean fu_keyring_get_release_flags (XbNode *release,
|
||||
FwupdReleaseFlags *flags,
|
||||
GError **error);
|
||||
|
106
src/fu-keyring.c
106
src/fu-keyring.c
@ -1,106 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2017 Richard Hughes <richard@hughsie.com>
|
||||
*
|
||||
* SPDX-License-Identifier: LGPL-2.1+
|
||||
*/
|
||||
|
||||
#define G_LOG_DOMAIN "FuKeyring"
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "fwupd-error.h"
|
||||
|
||||
#include "fu-keyring.h"
|
||||
|
||||
typedef struct {
|
||||
gchar *name;
|
||||
} FuKeyringPrivate;
|
||||
|
||||
G_DEFINE_TYPE_WITH_PRIVATE (FuKeyring, fu_keyring, G_TYPE_OBJECT)
|
||||
#define GET_PRIVATE(o) (fu_keyring_get_instance_private (o))
|
||||
|
||||
gboolean
|
||||
fu_keyring_setup (FuKeyring *keyring, GError **error)
|
||||
{
|
||||
FuKeyringClass *klass = FU_KEYRING_GET_CLASS (keyring);
|
||||
g_return_val_if_fail (FU_IS_KEYRING (keyring), FALSE);
|
||||
return klass->setup (keyring, error);
|
||||
}
|
||||
|
||||
gboolean
|
||||
fu_keyring_add_public_keys (FuKeyring *keyring, const gchar *path, GError **error)
|
||||
{
|
||||
FuKeyringClass *klass = FU_KEYRING_GET_CLASS (keyring);
|
||||
g_return_val_if_fail (FU_IS_KEYRING (keyring), FALSE);
|
||||
g_return_val_if_fail (path != NULL, FALSE);
|
||||
return klass->add_public_keys (keyring, path, error);
|
||||
}
|
||||
|
||||
FuKeyringResult *
|
||||
fu_keyring_verify_data (FuKeyring *keyring,
|
||||
GBytes *blob,
|
||||
GBytes *blob_signature,
|
||||
FuKeyringVerifyFlags flags,
|
||||
GError **error)
|
||||
{
|
||||
FuKeyringClass *klass = FU_KEYRING_GET_CLASS (keyring);
|
||||
g_return_val_if_fail (FU_IS_KEYRING (keyring), NULL);
|
||||
g_return_val_if_fail (blob != NULL, NULL);
|
||||
g_return_val_if_fail (blob_signature != NULL, NULL);
|
||||
return klass->verify_data (keyring, blob, blob_signature, flags, error);
|
||||
}
|
||||
|
||||
GBytes *
|
||||
fu_keyring_sign_data (FuKeyring *keyring,
|
||||
GBytes *blob,
|
||||
FuKeyringSignFlags flags,
|
||||
GError **error)
|
||||
{
|
||||
FuKeyringClass *klass = FU_KEYRING_GET_CLASS (keyring);
|
||||
g_return_val_if_fail (FU_IS_KEYRING (keyring), NULL);
|
||||
g_return_val_if_fail (blob != NULL, NULL);
|
||||
if (klass->sign_data == NULL) {
|
||||
g_set_error_literal (error,
|
||||
FWUPD_ERROR,
|
||||
FWUPD_ERROR_NOT_SUPPORTED,
|
||||
"signing data is not supported");
|
||||
return NULL;
|
||||
}
|
||||
return klass->sign_data (keyring, blob, flags, error);
|
||||
}
|
||||
|
||||
const gchar *
|
||||
fu_keyring_get_name (FuKeyring *self)
|
||||
{
|
||||
FuKeyringPrivate *priv = GET_PRIVATE (self);
|
||||
return priv->name;
|
||||
}
|
||||
|
||||
void
|
||||
fu_keyring_set_name (FuKeyring *self, const gchar *name)
|
||||
{
|
||||
FuKeyringPrivate *priv = GET_PRIVATE (self);
|
||||
g_free (priv->name);
|
||||
priv->name = g_strdup (name);
|
||||
}
|
||||
|
||||
static void
|
||||
fu_keyring_finalize (GObject *object)
|
||||
{
|
||||
FuKeyring *self = FU_KEYRING (object);
|
||||
FuKeyringPrivate *priv = GET_PRIVATE (self);
|
||||
g_free (priv->name);
|
||||
G_OBJECT_CLASS (fu_keyring_parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
static void
|
||||
fu_keyring_class_init (FuKeyringClass *klass)
|
||||
{
|
||||
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
||||
object_class->finalize = fu_keyring_finalize;
|
||||
}
|
||||
|
||||
static void
|
||||
fu_keyring_init (FuKeyring *keyring)
|
||||
{
|
||||
}
|
@ -1,84 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2017 Richard Hughes <richard@hughsie.com>
|
||||
*
|
||||
* SPDX-License-Identifier: LGPL-2.1+
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <glib-object.h>
|
||||
#include <gio/gio.h>
|
||||
|
||||
#include "fu-keyring-result.h"
|
||||
|
||||
#define FU_TYPE_KEYRING (fu_keyring_get_type ())
|
||||
G_DECLARE_DERIVABLE_TYPE (FuKeyring, fu_keyring, FU, KEYRING, GObject)
|
||||
|
||||
/**
|
||||
* FuKeyringVerifyFlags:
|
||||
* @FU_KEYRING_VERIFY_FLAG_NONE: No flags set
|
||||
* @FU_KEYRING_VERIFY_FLAG_USE_CLIENT_CERT: Use client certificate to verify
|
||||
* @FU_KEYRING_VERIFY_FLAG_DISABLE_TIME_CHECKS: Disable checking of validity periods
|
||||
*
|
||||
* The flags to use when interacting with a keyring
|
||||
**/
|
||||
typedef enum {
|
||||
FU_KEYRING_VERIFY_FLAG_NONE = 0,
|
||||
FU_KEYRING_VERIFY_FLAG_USE_CLIENT_CERT = 1 << 1,
|
||||
FU_KEYRING_VERIFY_FLAG_DISABLE_TIME_CHECKS = 1 << 2,
|
||||
/*< private >*/
|
||||
FU_KEYRING_VERIFY_FLAG_LAST
|
||||
} FuKeyringVerifyFlags;
|
||||
|
||||
/**
|
||||
* FuKeyringSignFlags:
|
||||
* @FU_KEYRING_SIGN_FLAG_NONE: No flags set
|
||||
* @FU_KEYRING_SIGN_FLAG_ADD_TIMESTAMP: Add a timestamp
|
||||
* @FU_KEYRING_SIGN_FLAG_ADD_CERT: Add a certificate
|
||||
*
|
||||
* The flags to when signing a binary
|
||||
**/
|
||||
typedef enum {
|
||||
FU_KEYRING_SIGN_FLAG_NONE = 0,
|
||||
FU_KEYRING_SIGN_FLAG_ADD_TIMESTAMP = 1 << 0,
|
||||
FU_KEYRING_SIGN_FLAG_ADD_CERT = 1 << 1,
|
||||
/*< private >*/
|
||||
FU_KEYRING_SIGN_FLAG_LAST
|
||||
} FuKeyringSignFlags;
|
||||
|
||||
struct _FuKeyringClass
|
||||
{
|
||||
GObjectClass parent_class;
|
||||
gboolean (*setup) (FuKeyring *keyring,
|
||||
GError **error);
|
||||
gboolean (*add_public_keys) (FuKeyring *keyring,
|
||||
const gchar *path,
|
||||
GError **error);
|
||||
FuKeyringResult *(*verify_data) (FuKeyring *keyring,
|
||||
GBytes *payload,
|
||||
GBytes *payload_signature,
|
||||
FuKeyringVerifyFlags flags,
|
||||
GError **error);
|
||||
GBytes *(*sign_data) (FuKeyring *keyring,
|
||||
GBytes *payload,
|
||||
FuKeyringSignFlags flags,
|
||||
GError **error);
|
||||
};
|
||||
|
||||
gboolean fu_keyring_setup (FuKeyring *keyring,
|
||||
GError **error);
|
||||
gboolean fu_keyring_add_public_keys (FuKeyring *keyring,
|
||||
const gchar *path,
|
||||
GError **error);
|
||||
FuKeyringResult *fu_keyring_verify_data (FuKeyring *keyring,
|
||||
GBytes *blob,
|
||||
GBytes *blob_signature,
|
||||
FuKeyringVerifyFlags flags,
|
||||
GError **error);
|
||||
GBytes *fu_keyring_sign_data (FuKeyring *keyring,
|
||||
GBytes *blob,
|
||||
FuKeyringSignFlags flags,
|
||||
GError **error);
|
||||
const gchar *fu_keyring_get_name (FuKeyring *self);
|
||||
void fu_keyring_set_name (FuKeyring *self,
|
||||
const gchar *name);
|
@ -17,6 +17,7 @@
|
||||
#include <polkit/polkit.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <jcat.h>
|
||||
|
||||
#include "fwupd-device-private.h"
|
||||
#include "fwupd-release-private.h"
|
||||
@ -900,10 +901,10 @@ fu_main_daemon_method_call (GDBusConnection *connection, const gchar *sender,
|
||||
g_debug ("got option %s", prop_key);
|
||||
if (g_strcmp0 (prop_key, "add-timestamp") == 0 &&
|
||||
g_variant_get_boolean (prop_value) == TRUE)
|
||||
helper->flags |= FU_KEYRING_SIGN_FLAG_ADD_TIMESTAMP;
|
||||
helper->flags |= JCAT_SIGN_FLAG_ADD_TIMESTAMP;
|
||||
if (g_strcmp0 (prop_key, "add-cert") == 0 &&
|
||||
g_variant_get_boolean (prop_value) == TRUE)
|
||||
helper->flags |= FU_KEYRING_SIGN_FLAG_ADD_CERT;
|
||||
helper->flags |= JCAT_SIGN_FLAG_ADD_CERT;
|
||||
g_variant_unref (prop_value);
|
||||
}
|
||||
|
||||
|
@ -19,7 +19,6 @@
|
||||
#include "fu-device-list.h"
|
||||
#include "fu-device-private.h"
|
||||
#include "fu-engine.h"
|
||||
#include "fu-keyring.h"
|
||||
#include "fu-history.h"
|
||||
#include "fu-install-task.h"
|
||||
#include "fu-plugin-private.h"
|
||||
@ -28,13 +27,6 @@
|
||||
#include "fu-hash.h"
|
||||
#include "fu-smbios-private.h"
|
||||
|
||||
#ifdef ENABLE_GPG
|
||||
#include "fu-keyring-gpg.h"
|
||||
#endif
|
||||
#ifdef ENABLE_PKCS7
|
||||
#include "fu-keyring-pkcs7.h"
|
||||
#endif
|
||||
|
||||
typedef struct {
|
||||
FuPlugin *plugin;
|
||||
} FuTest;
|
||||
@ -2591,185 +2583,6 @@ fu_history_func (gconstpointer user_data)
|
||||
g_assert_cmpstr (g_ptr_array_index (approved_firmware, 1), ==, "bar");
|
||||
}
|
||||
|
||||
static void
|
||||
fu_keyring_gpg_func (gconstpointer user_data)
|
||||
{
|
||||
#ifdef ENABLE_GPG
|
||||
gboolean ret;
|
||||
g_autofree gchar *fw_fail = NULL;
|
||||
g_autofree gchar *fw_pass = NULL;
|
||||
g_autofree gchar *pki_dir = NULL;
|
||||
g_autoptr(FuKeyring) keyring = NULL;
|
||||
g_autoptr(FuKeyringResult) result_fail = NULL;
|
||||
g_autoptr(FuKeyringResult) result_pass = NULL;
|
||||
g_autoptr(GBytes) blob_fail = NULL;
|
||||
g_autoptr(GBytes) blob_pass = NULL;
|
||||
g_autoptr(GBytes) blob_sig = NULL;
|
||||
g_autoptr(GError) error = NULL;
|
||||
const gchar *sig_gpgme =
|
||||
"-----BEGIN PGP SIGNATURE-----\n"
|
||||
"Version: GnuPG v1\n\n"
|
||||
"iQEcBAABCAAGBQJVt0B4AAoJEEim2A5FOLrCFb8IAK+QTLY34Wu8xZ8nl6p3JdMu"
|
||||
"HOaifXAmX7291UrsFRwdabU2m65pqxQLwcoFrqGv738KuaKtu4oIwo9LIrmmTbEh"
|
||||
"IID8uszxBt0bMdcIHrvwd+ADx+MqL4hR3guXEE3YOBTLvv2RF1UBcJPInNf/7Ui1"
|
||||
"3lW1c3trL8RAJyx1B5RdKqAMlyfwiuvKM5oT4SN4uRSbQf+9mt78ZSWfJVZZH/RR"
|
||||
"H9q7PzR5GdmbsRPM0DgC27Trvqjo3MzoVtoLjIyEb/aWqyulUbnJUNKPYTnZgkzM"
|
||||
"v2yVofWKIM3e3wX5+MOtf6EV58mWa2cHJQ4MCYmpKxbIvAIZagZ4c9A8BA6tQWg="
|
||||
"=fkit\n"
|
||||
"-----END PGP SIGNATURE-----\n";
|
||||
|
||||
/* add keys to keyring */
|
||||
keyring = fu_keyring_gpg_new ();
|
||||
ret = fu_keyring_setup (keyring, &error);
|
||||
g_assert_no_error (error);
|
||||
g_assert_true (ret);
|
||||
pki_dir = g_build_filename (TESTDATADIR_SRC, "pki", NULL);
|
||||
ret = fu_keyring_add_public_keys (keyring, pki_dir, &error);
|
||||
g_assert_no_error (error);
|
||||
g_assert_true (ret);
|
||||
|
||||
/* verify with GnuPG */
|
||||
fw_pass = g_build_filename (TESTDATADIR_SRC, "colorhug", "firmware.bin", NULL);
|
||||
blob_pass = fu_common_get_contents_bytes (fw_pass, &error);
|
||||
g_assert_no_error (error);
|
||||
g_assert_nonnull (blob_pass);
|
||||
blob_sig = g_bytes_new_static (sig_gpgme, strlen (sig_gpgme));
|
||||
result_pass = fu_keyring_verify_data (keyring, blob_pass, blob_sig,
|
||||
FU_KEYRING_VERIFY_FLAG_NONE,
|
||||
&error);
|
||||
g_assert_no_error (error);
|
||||
g_assert_nonnull (result_pass);
|
||||
g_assert_cmpint (fu_keyring_result_get_timestamp (result_pass), == , 1438072952);
|
||||
g_assert_cmpstr (fu_keyring_result_get_authority (result_pass), == ,
|
||||
"3FC6B804410ED0840D8F2F9748A6D80E4538BAC2");
|
||||
|
||||
/* verify will fail with GnuPG */
|
||||
fw_fail = g_build_filename (TESTDATADIR_DST, "colorhug", "colorhug-als-3.0.2.cab", NULL);
|
||||
blob_fail = fu_common_get_contents_bytes (fw_fail, &error);
|
||||
g_assert_no_error (error);
|
||||
g_assert_nonnull (blob_fail);
|
||||
result_fail = fu_keyring_verify_data (keyring, blob_fail, blob_sig,
|
||||
FU_KEYRING_VERIFY_FLAG_NONE, &error);
|
||||
g_assert_error (error, FWUPD_ERROR, FWUPD_ERROR_SIGNATURE_INVALID);
|
||||
g_assert_null (result_fail);
|
||||
g_clear_error (&error);
|
||||
#else
|
||||
g_test_skip ("no GnuPG support enabled");
|
||||
#endif
|
||||
}
|
||||
|
||||
static void
|
||||
fu_keyring_pkcs7_func (gconstpointer user_data)
|
||||
{
|
||||
#ifdef ENABLE_PKCS7
|
||||
gboolean ret;
|
||||
g_autofree gchar *fw_fail = NULL;
|
||||
g_autofree gchar *fw_pass = NULL;
|
||||
g_autofree gchar *pki_dir = NULL;
|
||||
g_autofree gchar *sig_fn = NULL;
|
||||
g_autofree gchar *sig_fn2 = NULL;
|
||||
g_autoptr(FuKeyring) keyring = NULL;
|
||||
g_autoptr(FuKeyringResult) result_fail = NULL;
|
||||
g_autoptr(FuKeyringResult) result_pass = NULL;
|
||||
g_autoptr(GBytes) blob_fail = NULL;
|
||||
g_autoptr(GBytes) blob_pass = NULL;
|
||||
g_autoptr(GBytes) blob_sig = NULL;
|
||||
g_autoptr(GBytes) blob_sig2 = NULL;
|
||||
g_autoptr(GError) error = NULL;
|
||||
|
||||
/* add keys to keyring */
|
||||
keyring = fu_keyring_pkcs7_new ();
|
||||
ret = fu_keyring_setup (keyring, &error);
|
||||
g_assert_no_error (error);
|
||||
g_assert_true (ret);
|
||||
pki_dir = g_build_filename (TESTDATADIR_SRC, "pki", NULL);
|
||||
ret = fu_keyring_add_public_keys (keyring, pki_dir, &error);
|
||||
g_assert_no_error (error);
|
||||
g_assert_true (ret);
|
||||
|
||||
/* verify with a signature from the old LVFS */
|
||||
fw_pass = g_build_filename (TESTDATADIR_SRC, "colorhug", "firmware.bin", NULL);
|
||||
blob_pass = fu_common_get_contents_bytes (fw_pass, &error);
|
||||
g_assert_no_error (error);
|
||||
g_assert_nonnull (blob_pass);
|
||||
sig_fn = g_build_filename (TESTDATADIR_SRC, "colorhug", "firmware.bin.p7b", NULL);
|
||||
blob_sig = fu_common_get_contents_bytes (sig_fn, &error);
|
||||
g_assert_no_error (error);
|
||||
g_assert_nonnull (blob_sig);
|
||||
result_pass = fu_keyring_verify_data (keyring, blob_pass, blob_sig,
|
||||
FU_KEYRING_VERIFY_FLAG_DISABLE_TIME_CHECKS,
|
||||
&error);
|
||||
g_assert_no_error (error);
|
||||
g_assert_nonnull (result_pass);
|
||||
g_assert_cmpint (fu_keyring_result_get_timestamp (result_pass), >= , 1502871248);
|
||||
g_assert_cmpstr (fu_keyring_result_get_authority (result_pass), == , "O=Linux Vendor Firmware Project,CN=LVFS CA");
|
||||
|
||||
/* verify will fail with a self-signed signature */
|
||||
sig_fn2 = g_build_filename (TESTDATADIR_DST, "colorhug", "firmware.bin.p7c", NULL);
|
||||
blob_sig2 = fu_common_get_contents_bytes (sig_fn2, &error);
|
||||
g_assert_no_error (error);
|
||||
g_assert_nonnull (blob_sig2);
|
||||
result_fail = fu_keyring_verify_data (keyring, blob_pass, blob_sig2,
|
||||
FU_KEYRING_VERIFY_FLAG_NONE, &error);
|
||||
g_assert_error (error, FWUPD_ERROR, FWUPD_ERROR_SIGNATURE_INVALID);
|
||||
g_assert_null (result_fail);
|
||||
g_clear_error (&error);
|
||||
|
||||
/* verify will fail with valid signature and different data */
|
||||
fw_fail = g_build_filename (TESTDATADIR_DST, "colorhug", "colorhug-als-3.0.2.cab", NULL);
|
||||
blob_fail = fu_common_get_contents_bytes (fw_fail, &error);
|
||||
g_assert_no_error (error);
|
||||
g_assert_nonnull (blob_fail);
|
||||
result_fail = fu_keyring_verify_data (keyring, blob_fail, blob_sig,
|
||||
FU_KEYRING_VERIFY_FLAG_NONE, &error);
|
||||
g_assert_error (error, FWUPD_ERROR, FWUPD_ERROR_SIGNATURE_INVALID);
|
||||
g_assert_null (result_fail);
|
||||
g_clear_error (&error);
|
||||
#else
|
||||
g_test_skip ("no GnuTLS support enabled");
|
||||
#endif
|
||||
}
|
||||
|
||||
static void
|
||||
fu_keyring_pkcs7_self_signed_func (gconstpointer user_data)
|
||||
{
|
||||
#ifdef ENABLE_PKCS7
|
||||
gboolean ret;
|
||||
g_autoptr(FuKeyring) kr = NULL;
|
||||
g_autoptr(FuKeyringResult) kr_result = NULL;
|
||||
g_autoptr(GBytes) payload = NULL;
|
||||
g_autoptr(GBytes) signature = NULL;
|
||||
g_autoptr(GError) error = NULL;
|
||||
|
||||
#ifndef HAVE_GNUTLS_3_6_0
|
||||
/* required to create the private key correctly */
|
||||
g_test_skip ("GnuTLS version too old");
|
||||
return;
|
||||
#endif
|
||||
|
||||
/* create detached signature and verify */
|
||||
kr = fu_keyring_pkcs7_new ();
|
||||
ret = fu_keyring_setup (kr, &error);
|
||||
g_assert_no_error (error);
|
||||
g_assert_true (ret);
|
||||
payload = fu_common_get_contents_bytes ("/etc/machine-id", &error);
|
||||
g_assert_no_error (error);
|
||||
g_assert_nonnull (payload);
|
||||
signature = fu_keyring_sign_data (kr, payload, FU_KEYRING_SIGN_FLAG_ADD_TIMESTAMP, &error);
|
||||
g_assert_no_error (error);
|
||||
g_assert_nonnull (signature);
|
||||
ret = fu_common_set_contents_bytes ("/tmp/test.p7b", signature, &error);
|
||||
g_assert_no_error (error);
|
||||
g_assert_true (ret);
|
||||
kr_result = fu_keyring_verify_data (kr, payload, signature,
|
||||
FU_KEYRING_VERIFY_FLAG_USE_CLIENT_CERT, &error);
|
||||
g_assert_no_error (error);
|
||||
g_assert_nonnull (kr_result);
|
||||
#else
|
||||
g_test_skip ("no GnuTLS support enabled");
|
||||
#endif
|
||||
}
|
||||
|
||||
static GBytes *
|
||||
_build_cab (GCabCompression compression, ...)
|
||||
{
|
||||
@ -3209,11 +3022,5 @@ main (int argc, char **argv)
|
||||
fu_plugin_list_func);
|
||||
g_test_add_data_func ("/fwupd/plugin-list{depsolve}", self,
|
||||
fu_plugin_list_depsolve_func);
|
||||
g_test_add_data_func ("/fwupd/keyring{gpg}", self,
|
||||
fu_keyring_gpg_func);
|
||||
g_test_add_data_func ("/fwupd/keyring{pkcs7}", self,
|
||||
fu_keyring_pkcs7_func);
|
||||
g_test_add_data_func ("/fwupd/keyring{pkcs7-self-signed}", self,
|
||||
fu_keyring_pkcs7_self_signed_func);
|
||||
return g_test_run ();
|
||||
}
|
||||
|
@ -19,6 +19,7 @@
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <libsoup/soup.h>
|
||||
#include <jcat.h>
|
||||
|
||||
#include "fu-device-private.h"
|
||||
#include "fu-engine.h"
|
||||
@ -1376,8 +1377,8 @@ fu_util_self_sign (FuUtilPrivate *priv, gchar **values, GError **error)
|
||||
if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_NONE, error))
|
||||
return FALSE;
|
||||
sig = fu_engine_self_sign (priv->engine, values[0],
|
||||
FU_KEYRING_SIGN_FLAG_ADD_TIMESTAMP |
|
||||
FU_KEYRING_SIGN_FLAG_ADD_CERT, error);
|
||||
JCAT_SIGN_FLAG_ADD_TIMESTAMP |
|
||||
JCAT_SIGN_FLAG_ADD_CERT, error);
|
||||
if (sig == NULL)
|
||||
return FALSE;
|
||||
g_print ("%s\n", sig);
|
||||
|
@ -4,21 +4,8 @@ if build_daemon
|
||||
)
|
||||
endif
|
||||
|
||||
keyring_deps = []
|
||||
keyring_src = []
|
||||
systemd_src = []
|
||||
|
||||
if get_option('gpg')
|
||||
keyring_src += 'fu-keyring-gpg.c'
|
||||
keyring_deps += gpgme
|
||||
keyring_deps += gpgerror
|
||||
endif
|
||||
|
||||
if get_option('pkcs7')
|
||||
keyring_src += 'fu-keyring-pkcs7.c'
|
||||
keyring_deps += gnutls
|
||||
endif
|
||||
|
||||
if get_option('systemd')
|
||||
systemd_src += 'fu-systemd.c'
|
||||
endif
|
||||
@ -132,7 +119,6 @@ fwupdtool = executable(
|
||||
export_dynamic : true,
|
||||
sources : [
|
||||
'fu-tool.c',
|
||||
keyring_src,
|
||||
'fu-config.c',
|
||||
'fu-debug.c',
|
||||
'fu-device-list.c',
|
||||
@ -141,8 +127,6 @@ fwupdtool = executable(
|
||||
'fu-history.c',
|
||||
'fu-idle.c',
|
||||
'fu-install-task.c',
|
||||
'fu-keyring.c',
|
||||
'fu-keyring-result.c',
|
||||
'fu-keyring-utils.c',
|
||||
'fu-plugin-list.c',
|
||||
'fu-progressbar.c',
|
||||
@ -156,7 +140,7 @@ fwupdtool = executable(
|
||||
fwupdplugin_incdir,
|
||||
],
|
||||
dependencies : [
|
||||
keyring_deps,
|
||||
libjcat,
|
||||
libxmlb,
|
||||
libgcab,
|
||||
giounix,
|
||||
@ -233,7 +217,6 @@ executable(
|
||||
resources_src,
|
||||
fu_hash,
|
||||
sources : [
|
||||
keyring_src,
|
||||
'fu-config.c',
|
||||
'fu-debug.c',
|
||||
'fu-device-list.c',
|
||||
@ -242,8 +225,6 @@ executable(
|
||||
'fu-history.c',
|
||||
'fu-idle.c',
|
||||
'fu-install-task.c',
|
||||
'fu-keyring.c',
|
||||
'fu-keyring-result.c',
|
||||
'fu-keyring-utils.c',
|
||||
'fu-main.c',
|
||||
'fu-plugin-list.c',
|
||||
@ -256,7 +237,7 @@ executable(
|
||||
fwupdplugin_incdir,
|
||||
],
|
||||
dependencies : [
|
||||
keyring_deps,
|
||||
libjcat,
|
||||
libxmlb,
|
||||
libgcab,
|
||||
giounix,
|
||||
@ -272,7 +253,7 @@ executable(
|
||||
],
|
||||
link_with : [
|
||||
fwupd,
|
||||
fwupdplugin
|
||||
fwupdplugin,
|
||||
],
|
||||
c_args : [
|
||||
'-DFU_OFFLINE_DESTDIR=""',
|
||||
@ -293,7 +274,6 @@ if get_option('tests')
|
||||
test_deps,
|
||||
fu_hash,
|
||||
sources : [
|
||||
keyring_src,
|
||||
'fu-config.c',
|
||||
'fu-device-list.c',
|
||||
'fu-engine.c',
|
||||
@ -301,8 +281,6 @@ if get_option('tests')
|
||||
'fu-history.c',
|
||||
'fu-idle.c',
|
||||
'fu-install-task.c',
|
||||
'fu-keyring.c',
|
||||
'fu-keyring-result.c',
|
||||
'fu-keyring-utils.c',
|
||||
'fu-plugin-list.c',
|
||||
'fu-progressbar.c',
|
||||
@ -316,7 +294,7 @@ if get_option('tests')
|
||||
fwupdplugin_incdir,
|
||||
],
|
||||
dependencies : [
|
||||
keyring_deps,
|
||||
libjcat,
|
||||
libxmlb,
|
||||
libgcab,
|
||||
giounix,
|
||||
|
1
subprojects/.gitignore
vendored
1
subprojects/.gitignore
vendored
@ -1,4 +1,5 @@
|
||||
gusb
|
||||
gcab
|
||||
flashrom
|
||||
libjcat
|
||||
libxmlb
|
||||
|
4
subprojects/libjcat.wrap
Normal file
4
subprojects/libjcat.wrap
Normal file
@ -0,0 +1,4 @@
|
||||
[wrap-git]
|
||||
directory = libjcat
|
||||
url = https://github.com/hughsie/libjcat.git
|
||||
revision = master
|
Loading…
Reference in New Issue
Block a user