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:
Richard Hughes 2020-02-25 12:47:50 +00:00
parent 86b0bae0c4
commit d5aab65f30
45 changed files with 463 additions and 2131 deletions

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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>

View File

@ -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

View File

@ -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"

View File

@ -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')
)

View File

@ -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=

View File

@ -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

View File

@ -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

View File

@ -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')

View File

@ -1 +0,0 @@
../../pki/GPG-KEY-Linux-Vendor-Firmware-Service

View File

@ -1 +0,0 @@
../../pki/LVFS-CA.pem

View File

@ -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@'],
)

View File

@ -1,4 +0,0 @@
organization = "Hughski Limited"
expiration_days = -1
email = "info@hughski.com"
signing_key

View File

@ -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;
}

View File

@ -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;

View File

@ -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 */

View File

@ -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");

View File

@ -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);

View File

@ -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,

View File

@ -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);
}

View File

@ -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;

View File

@ -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')

View File

@ -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)

View File

@ -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')

View File

@ -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

View File

@ -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

View File

@ -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,

View File

@ -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));
}

View File

@ -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);

View File

@ -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));
}

View File

@ -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);

View File

@ -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)
{
}

View File

@ -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);

View File

@ -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;
}

View File

@ -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);

View File

@ -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)
{
}

View File

@ -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);

View File

@ -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);
}

View File

@ -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 ();
}

View File

@ -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);

View File

@ -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,

View File

@ -1,4 +1,5 @@
gusb
gcab
flashrom
libjcat
libxmlb

4
subprojects/libjcat.wrap Normal file
View File

@ -0,0 +1,4 @@
[wrap-git]
directory = libjcat
url = https://github.com/hughsie/libjcat.git
revision = master