mirror of
https://git.proxmox.com/git/fwupd
synced 2025-05-26 02:42:46 +00:00

In some situations SELinux prevents fwupd from executing tpm2_pcrlist, but the failure mode is that the process just hangs and never completes. This causes systemd to time out the fwupd daemon startup and then errors to be shown in GNOME Software. To prevent this happening, add an optional timeout argument to fu_common_spawn_sync() and cancel the subprocess if it takes longer than this to complete. See https://bugzilla.redhat.com/show_bug.cgi?id=1665701 for details.
206 lines
5.0 KiB
C
206 lines
5.0 KiB
C
/*
|
|
* Copyright (C) 2018 Richard Hughes <richard@hughsie.com>
|
|
*
|
|
* SPDX-License-Identifier: LGPL-2.1+
|
|
*/
|
|
|
|
#include "config.h"
|
|
|
|
#include "fu-common.h"
|
|
#include "fu-uefi-pcrs.h"
|
|
|
|
typedef struct {
|
|
guint idx;
|
|
gchar *checksum;
|
|
} FuUefiPcrItem;
|
|
|
|
struct _FuUefiPcrs {
|
|
GObject parent_instance;
|
|
GPtrArray *items; /* of FuUefiPcrItem */
|
|
};
|
|
|
|
G_DEFINE_TYPE (FuUefiPcrs, fu_uefi_pcrs, G_TYPE_OBJECT)
|
|
|
|
static void
|
|
fu_uefi_pcrs_parse_line (const gchar *line, gpointer user_data)
|
|
{
|
|
FuUefiPcrs *self = FU_UEFI_PCRS (user_data);
|
|
FuUefiPcrItem *item;
|
|
guint64 idx;
|
|
g_autofree gchar *idxstr = NULL;
|
|
g_auto(GStrv) split = NULL;
|
|
g_autoptr(GString) str = NULL;
|
|
|
|
/* split into index:hash */
|
|
if (line == NULL || line[0] == '\0')
|
|
return;
|
|
split = g_strsplit (line, ":", 2);
|
|
if (g_strv_length (split) != 2) {
|
|
g_debug ("unexpected format, skipping: %s", line);
|
|
return;
|
|
}
|
|
|
|
/* get index */
|
|
idxstr = fu_common_strstrip (split[0]);
|
|
idx = fu_common_strtoull (idxstr);
|
|
if (idx > 64) {
|
|
g_debug ("unexpected index, skipping: %s", idxstr);
|
|
return;
|
|
}
|
|
|
|
/* parse hash */
|
|
str = g_string_new (split[1]);
|
|
if (str->len < 16)
|
|
return;
|
|
fu_common_string_replace (str, " ", "");
|
|
g_string_ascii_down (str);
|
|
item = g_new0 (FuUefiPcrItem, 1);
|
|
item->idx = idx;
|
|
item->checksum = g_string_free (g_steal_pointer (&str), FALSE);
|
|
g_ptr_array_add (self->items, item);
|
|
g_debug ("added PCR-%02u=%s", item->idx, item->checksum);
|
|
}
|
|
|
|
static gboolean
|
|
fu_uefi_pcrs_setup_dummy (FuUefiPcrs *self, const gchar *test_yaml, GError **error)
|
|
{
|
|
g_auto(GStrv) lines = g_strsplit (test_yaml, "\n", -1);
|
|
for (guint i = 0; lines[i] != NULL; i++)
|
|
fu_uefi_pcrs_parse_line (lines[i], self);
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
fu_uefi_pcrs_setup_tpm12 (FuUefiPcrs *self, const gchar *fn_pcrs, GError **error)
|
|
{
|
|
g_auto(GStrv) lines = NULL;
|
|
g_autofree gchar *buf_pcrs = NULL;
|
|
|
|
/* get entire contents */
|
|
if (!g_file_get_contents (fn_pcrs, &buf_pcrs, NULL, error))
|
|
return FALSE;
|
|
|
|
/* find PCR lines */
|
|
lines = g_strsplit (buf_pcrs, "\n", -1);
|
|
for (guint i = 0; lines[i] != NULL; i++) {
|
|
if (g_str_has_prefix (lines[i], "PCR-"))
|
|
fu_uefi_pcrs_parse_line (lines[i] + 4, self);
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
fu_uefi_pcrs_setup_tpm20 (FuUefiPcrs *self, const gchar *argv0, GError **error)
|
|
{
|
|
const gchar *argv[] = { argv0, NULL };
|
|
return fu_common_spawn_sync (argv, fu_uefi_pcrs_parse_line, self, 1500, NULL, error);
|
|
}
|
|
|
|
gboolean
|
|
fu_uefi_pcrs_setup (FuUefiPcrs *self, GError **error)
|
|
{
|
|
g_autofree gchar *devpath = NULL;
|
|
g_autofree gchar *sysfstpmdir = NULL;
|
|
g_autofree gchar *fn_pcrs = NULL;
|
|
const gchar *test_yaml = g_getenv ("FWUPD_UEFI_TPM2_YAML_DATA");
|
|
|
|
g_return_val_if_fail (FU_IS_UEFI_PCRS (self), FALSE);
|
|
|
|
/* check the TPM device exists at all */
|
|
sysfstpmdir = fu_common_get_path (FU_PATH_KIND_SYSFSDIR_TPM);
|
|
devpath = g_build_filename (sysfstpmdir, "tpm0", NULL);
|
|
if (!g_file_test (devpath, G_FILE_TEST_EXISTS)) {
|
|
g_set_error_literal (error,
|
|
G_IO_ERROR,
|
|
G_IO_ERROR_NOT_SUPPORTED,
|
|
"no TPM device found");
|
|
return FALSE;
|
|
}
|
|
fn_pcrs = g_build_filename (devpath, "pcrs", NULL);
|
|
|
|
/* fake device */
|
|
if (test_yaml != NULL) {
|
|
if (!fu_uefi_pcrs_setup_dummy (self, test_yaml, error))
|
|
return FALSE;
|
|
|
|
/* look for TPM 1.2 */
|
|
} else if (g_file_test (fn_pcrs, G_FILE_TEST_EXISTS)) {
|
|
if (!fu_uefi_pcrs_setup_tpm12 (self, fn_pcrs, error))
|
|
return FALSE;
|
|
|
|
/* assume TPM 2.0 */
|
|
} else {
|
|
g_autofree gchar *argv0 = NULL;
|
|
|
|
/* old name, then new name */
|
|
argv0 = fu_common_find_program_in_path ("tpm2_listpcrs", NULL);
|
|
if (argv0 == NULL)
|
|
argv0 = fu_common_find_program_in_path ("tpm2_pcrlist", error);
|
|
if (argv0 == NULL)
|
|
return FALSE;
|
|
if (!fu_uefi_pcrs_setup_tpm20 (self, argv0, error))
|
|
return FALSE;
|
|
}
|
|
|
|
/* check we got anything */
|
|
if (self->items->len == 0) {
|
|
g_set_error_literal (error,
|
|
G_IO_ERROR,
|
|
G_IO_ERROR_NOT_SUPPORTED,
|
|
"no TPMxx measurements found");
|
|
return FALSE;
|
|
}
|
|
|
|
/* success */
|
|
return TRUE;
|
|
}
|
|
|
|
GPtrArray *
|
|
fu_uefi_pcrs_get_checksums (FuUefiPcrs *self, guint idx)
|
|
{
|
|
g_autoptr(GPtrArray) array = g_ptr_array_new_with_free_func (g_free);
|
|
g_return_val_if_fail (FU_IS_UEFI_PCRS (self), NULL);
|
|
for (guint i = 0; i < self->items->len; i++) {
|
|
FuUefiPcrItem *item = g_ptr_array_index (self->items, i);
|
|
if (item->idx == idx)
|
|
g_ptr_array_add (array, g_strdup (item->checksum));
|
|
}
|
|
return g_steal_pointer (&array);
|
|
}
|
|
|
|
static void
|
|
fu_uefi_pcrs_item_free (FuUefiPcrItem *item)
|
|
{
|
|
g_free (item->checksum);
|
|
g_free (item);
|
|
}
|
|
|
|
static void
|
|
fu_uefi_pcrs_finalize (GObject *object)
|
|
{
|
|
FuUefiPcrs *self = FU_UEFI_PCRS (object);
|
|
g_ptr_array_unref (self->items);
|
|
G_OBJECT_CLASS (fu_uefi_pcrs_parent_class)->finalize (object);
|
|
}
|
|
|
|
static void
|
|
fu_uefi_pcrs_class_init (FuUefiPcrsClass *klass)
|
|
{
|
|
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
|
object_class->finalize = fu_uefi_pcrs_finalize;
|
|
}
|
|
|
|
static void
|
|
fu_uefi_pcrs_init (FuUefiPcrs *self)
|
|
{
|
|
self->items = g_ptr_array_new_with_free_func ((GDestroyNotify) fu_uefi_pcrs_item_free);
|
|
}
|
|
|
|
FuUefiPcrs *
|
|
fu_uefi_pcrs_new (void)
|
|
{
|
|
FuUefiPcrs *self;
|
|
self = g_object_new (FU_TYPE_UEFI_PCRS, NULL);
|
|
return FU_UEFI_PCRS (self);
|
|
}
|