mirror of
https://git.proxmox.com/git/fwupd
synced 2025-07-27 04:04:05 +00:00
Move the TPM handling into the TPM place
The fact that it currently lives in uefi-capsule is a historical accident, and it doesn't really belong to tpm-eventlog either. Fixes some of https://github.com/fwupd/fwupd/issues/3901
This commit is contained in:
parent
23b1d20294
commit
e29c74180c
@ -8,10 +8,12 @@
|
|||||||
|
|
||||||
#include <fwupdplugin.h>
|
#include <fwupdplugin.h>
|
||||||
|
|
||||||
#include "fu-tpm-device.h"
|
#include "fu-tpm-v1-device.h"
|
||||||
|
#include "fu-tpm-v2-device.h"
|
||||||
|
|
||||||
struct FuPluginData {
|
struct FuPluginData {
|
||||||
FuDevice *tpm_device;
|
FuTpmDevice *tpm_device;
|
||||||
|
FuDevice *bios_device;
|
||||||
gboolean has_tpm_v20;
|
gboolean has_tpm_v20;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -21,7 +23,7 @@ fu_plugin_init(FuPlugin *plugin)
|
|||||||
fu_plugin_alloc_data(plugin, sizeof(FuPluginData));
|
fu_plugin_alloc_data(plugin, sizeof(FuPluginData));
|
||||||
fu_plugin_set_build_hash(plugin, FU_BUILD_HASH);
|
fu_plugin_set_build_hash(plugin, FU_BUILD_HASH);
|
||||||
fu_plugin_add_udev_subsystem(plugin, "tpm");
|
fu_plugin_add_udev_subsystem(plugin, "tpm");
|
||||||
fu_plugin_add_device_gtype(plugin, FU_TYPE_TPM_DEVICE);
|
fu_plugin_add_device_gtype(plugin, FU_TYPE_TPM_V2_DEVICE);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
@ -30,6 +32,42 @@ fu_plugin_destroy(FuPlugin *plugin)
|
|||||||
FuPluginData *data = fu_plugin_get_data(plugin);
|
FuPluginData *data = fu_plugin_get_data(plugin);
|
||||||
if (data->tpm_device != NULL)
|
if (data->tpm_device != NULL)
|
||||||
g_object_unref(data->tpm_device);
|
g_object_unref(data->tpm_device);
|
||||||
|
if (data->bios_device != NULL)
|
||||||
|
g_object_unref(data->bios_device);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
fu_plugin_tpm_set_bios_pcr0s(FuPlugin *plugin)
|
||||||
|
{
|
||||||
|
FuPluginData *data = fu_plugin_get_data(plugin);
|
||||||
|
g_autoptr(GError) error_local = NULL;
|
||||||
|
g_autoptr(GPtrArray) pcr0s = NULL;
|
||||||
|
|
||||||
|
if (data->tpm_device == NULL)
|
||||||
|
return;
|
||||||
|
if (data->bios_device == NULL)
|
||||||
|
return;
|
||||||
|
|
||||||
|
/* get all the PCR0s */
|
||||||
|
pcr0s = fu_tpm_device_get_checksums(data->tpm_device, 0);
|
||||||
|
if (pcr0s->len == 0)
|
||||||
|
return;
|
||||||
|
for (guint i = 0; i < pcr0s->len; i++) {
|
||||||
|
const gchar *checksum = g_ptr_array_index(pcr0s, i);
|
||||||
|
fu_device_add_checksum(data->bios_device, checksum);
|
||||||
|
}
|
||||||
|
fu_device_add_flag(data->bios_device, FWUPD_DEVICE_FLAG_CAN_VERIFY);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* set the PCR0 as the device checksum */
|
||||||
|
void
|
||||||
|
fu_plugin_device_registered(FuPlugin *plugin, FuDevice *device)
|
||||||
|
{
|
||||||
|
FuPluginData *data = fu_plugin_get_data(plugin);
|
||||||
|
if (fu_device_has_instance_id(device, "main-system-firmware")) {
|
||||||
|
g_set_object(&data->bios_device, device);
|
||||||
|
fu_plugin_tpm_set_bios_pcr0s(plugin);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
@ -38,10 +76,13 @@ fu_plugin_device_added(FuPlugin *plugin, FuDevice *dev)
|
|||||||
FuPluginData *data = fu_plugin_get_data(plugin);
|
FuPluginData *data = fu_plugin_get_data(plugin);
|
||||||
const gchar *family = fu_tpm_device_get_family(FU_TPM_DEVICE(dev));
|
const gchar *family = fu_tpm_device_get_family(FU_TPM_DEVICE(dev));
|
||||||
|
|
||||||
g_set_object(&data->tpm_device, dev);
|
g_set_object(&data->tpm_device, FU_TPM_DEVICE(dev));
|
||||||
if (g_strcmp0(family, "2.0") == 0)
|
if (g_strcmp0(family, "2.0") == 0)
|
||||||
data->has_tpm_v20 = TRUE;
|
data->has_tpm_v20 = TRUE;
|
||||||
fu_plugin_add_report_metadata(plugin, "TpmFamily", family);
|
fu_plugin_add_report_metadata(plugin, "TpmFamily", family);
|
||||||
|
|
||||||
|
/* ensure */
|
||||||
|
fu_plugin_tpm_set_bios_pcr0s(plugin);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
@ -67,7 +108,28 @@ fu_plugin_add_security_attrs(FuPlugin *plugin, FuSecurityAttrs *attrs)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* success */
|
/* success */
|
||||||
fwupd_security_attr_add_guids(attr, fu_device_get_guids(data->tpm_device));
|
fwupd_security_attr_add_guids(attr, fu_device_get_guids(FU_DEVICE(data->tpm_device)));
|
||||||
fwupd_security_attr_add_flag(attr, FWUPD_SECURITY_ATTR_FLAG_SUCCESS);
|
fwupd_security_attr_add_flag(attr, FWUPD_SECURITY_ATTR_FLAG_SUCCESS);
|
||||||
fwupd_security_attr_set_result(attr, FWUPD_SECURITY_ATTR_RESULT_FOUND);
|
fwupd_security_attr_set_result(attr, FWUPD_SECURITY_ATTR_RESULT_FOUND);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
gboolean
|
||||||
|
fu_plugin_startup(FuPlugin *plugin, GError **error)
|
||||||
|
{
|
||||||
|
FuPluginData *data = fu_plugin_get_data(plugin);
|
||||||
|
g_autofree gchar *sysfstpmdir = NULL;
|
||||||
|
g_autofree gchar *fn_pcrs = NULL;
|
||||||
|
|
||||||
|
/* look for TPM v1.2 */
|
||||||
|
sysfstpmdir = fu_common_get_path(FU_PATH_KIND_SYSFSDIR_TPM);
|
||||||
|
fn_pcrs = g_build_filename(sysfstpmdir, "tmp0", "pcrs", NULL);
|
||||||
|
if (g_file_test(fn_pcrs, G_FILE_TEST_EXISTS) && g_getenv("FWUPD_FORCE_TPM2") == NULL) {
|
||||||
|
data->tpm_device = fu_tpm_v1_device_new(fu_plugin_get_context(plugin));
|
||||||
|
g_object_set(data->tpm_device, "device-file", fn_pcrs, NULL);
|
||||||
|
if (!fu_device_probe(FU_DEVICE(data->tpm_device), error))
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* success */
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
96
plugins/tpm/fu-self-test.c
Normal file
96
plugins/tpm/fu-self-test.c
Normal file
@ -0,0 +1,96 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2018 Richard Hughes <richard@hughsie.com>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: LGPL-2.1+
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
#include <fwupdplugin.h>
|
||||||
|
|
||||||
|
#include "fu-context-private.h"
|
||||||
|
#include "fu-tpm-v1-device.h"
|
||||||
|
#include "fu-tpm-v2-device.h"
|
||||||
|
|
||||||
|
static void
|
||||||
|
fu_tpm_device_1_2_func(void)
|
||||||
|
{
|
||||||
|
gboolean ret;
|
||||||
|
g_autoptr(FuContext) ctx = fu_context_new();
|
||||||
|
g_autoptr(FuTpmDevice) device = fu_tpm_v1_device_new(ctx);
|
||||||
|
g_autoptr(GError) error = NULL;
|
||||||
|
g_autoptr(GPtrArray) pcr0s = NULL;
|
||||||
|
g_autoptr(GPtrArray) pcrXs = NULL;
|
||||||
|
g_autofree gchar *testdatadir = NULL;
|
||||||
|
|
||||||
|
testdatadir = g_test_build_filename(G_TEST_DIST, "tests", "tpm0", "pcrs", NULL);
|
||||||
|
g_object_set(device, "device-file", testdatadir, NULL);
|
||||||
|
|
||||||
|
ret = fu_device_setup(FU_DEVICE(device), &error);
|
||||||
|
g_assert_no_error(error);
|
||||||
|
g_assert_true(ret);
|
||||||
|
pcr0s = fu_tpm_device_get_checksums(device, 0);
|
||||||
|
g_assert_nonnull(pcr0s);
|
||||||
|
g_assert_cmpint(pcr0s->len, ==, 1);
|
||||||
|
pcrXs = fu_tpm_device_get_checksums(device, 999);
|
||||||
|
g_assert_nonnull(pcrXs);
|
||||||
|
g_assert_cmpint(pcrXs->len, ==, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
fu_tpm_device_2_0_func(void)
|
||||||
|
{
|
||||||
|
g_autoptr(FuContext) ctx = fu_context_new();
|
||||||
|
g_autoptr(FuTpmDevice) device = fu_tpm_v2_device_new(ctx);
|
||||||
|
g_autoptr(GError) error = NULL;
|
||||||
|
g_autoptr(GPtrArray) pcr0s = NULL;
|
||||||
|
g_autoptr(GPtrArray) pcrXs = NULL;
|
||||||
|
const gchar *tpm_server_running = g_getenv("TPM_SERVER_RUNNING");
|
||||||
|
g_setenv("FWUPD_FORCE_TPM2", "1", TRUE);
|
||||||
|
|
||||||
|
#ifdef HAVE_GETUID
|
||||||
|
if (tpm_server_running == NULL && (getuid() != 0 || geteuid() != 0)) {
|
||||||
|
g_test_skip("TPM2.0 tests require simulated TPM2.0 running or need root access "
|
||||||
|
"with physical TPM");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (!fu_device_setup(FU_DEVICE(device), &error)) {
|
||||||
|
if (tpm_server_running == NULL &&
|
||||||
|
g_error_matches(error, FWUPD_ERROR, FWUPD_ERROR_NOT_FOUND)) {
|
||||||
|
g_test_skip("no physical or simulated TPM 2.0 device available");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
g_assert_no_error(error);
|
||||||
|
pcr0s = fu_tpm_device_get_checksums(device, 0);
|
||||||
|
g_assert_nonnull(pcr0s);
|
||||||
|
g_assert_cmpint(pcr0s->len, >=, 1);
|
||||||
|
pcrXs = fu_tpm_device_get_checksums(device, 999);
|
||||||
|
g_assert_nonnull(pcrXs);
|
||||||
|
g_assert_cmpint(pcrXs->len, ==, 0);
|
||||||
|
g_unsetenv("FWUPD_FORCE_TPM2");
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
main(int argc, char **argv)
|
||||||
|
{
|
||||||
|
g_autofree gchar *testdatadir = NULL;
|
||||||
|
|
||||||
|
g_test_init(&argc, &argv, NULL);
|
||||||
|
|
||||||
|
testdatadir = g_test_build_filename(G_TEST_DIST, "tests", NULL);
|
||||||
|
g_setenv("FWUPD_SYSFSFWDIR", testdatadir, TRUE);
|
||||||
|
g_setenv("FWUPD_SYSFSDRIVERDIR", testdatadir, TRUE);
|
||||||
|
g_setenv("FWUPD_UEFI_TEST", "1", TRUE);
|
||||||
|
|
||||||
|
/* only critical and error are fatal */
|
||||||
|
g_log_set_fatal_mask(NULL, G_LOG_LEVEL_ERROR | G_LOG_LEVEL_CRITICAL);
|
||||||
|
g_setenv("G_MESSAGES_DEBUG", "all", TRUE);
|
||||||
|
|
||||||
|
/* tests go here */
|
||||||
|
g_test_add_func("/tpm/pcrs1.2", fu_tpm_device_1_2_func);
|
||||||
|
g_test_add_func("/tpm/pcrs2.0", fu_tpm_device_2_0_func);
|
||||||
|
return g_test_run();
|
||||||
|
}
|
@ -6,264 +6,102 @@
|
|||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
|
||||||
#include <tss2/tss2_esys.h>
|
|
||||||
|
|
||||||
#include "fu-tpm-device.h"
|
#include "fu-tpm-device.h"
|
||||||
|
|
||||||
struct _FuTpmDevice {
|
typedef struct {
|
||||||
FuUdevDevice parent_instance;
|
guint idx;
|
||||||
|
gchar *checksum;
|
||||||
|
} FuTpmDevicePcrItem;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
gchar *family;
|
gchar *family;
|
||||||
};
|
GPtrArray *items; /* of FuTpmDevicePcrItem */
|
||||||
|
} FuTpmDevicePrivate;
|
||||||
|
|
||||||
G_DEFINE_TYPE(FuTpmDevice, fu_tpm_device, FU_TYPE_UDEV_DEVICE)
|
G_DEFINE_TYPE_WITH_PRIVATE(FuTpmDevice, fu_tpm_device, FU_TYPE_UDEV_DEVICE)
|
||||||
|
|
||||||
static void
|
#define GET_PRIVATE(o) (fu_tpm_device_get_instance_private(o))
|
||||||
Esys_Finalize_autoptr_cleanup(ESYS_CONTEXT *esys_context)
|
|
||||||
|
void
|
||||||
|
fu_tpm_device_set_family(FuTpmDevice *self, const gchar *family)
|
||||||
{
|
{
|
||||||
Esys_Finalize(&esys_context);
|
FuTpmDevicePrivate *priv = GET_PRIVATE(self);
|
||||||
|
g_return_if_fail(FU_IS_TPM_DEVICE(self));
|
||||||
|
priv->family = g_strdup(family);
|
||||||
}
|
}
|
||||||
G_DEFINE_AUTOPTR_CLEANUP_FUNC(ESYS_CONTEXT, Esys_Finalize_autoptr_cleanup)
|
|
||||||
|
|
||||||
const gchar *
|
const gchar *
|
||||||
fu_tpm_device_get_family(FuTpmDevice *self)
|
fu_tpm_device_get_family(FuTpmDevice *self)
|
||||||
{
|
{
|
||||||
return self->family;
|
FuTpmDevicePrivate *priv = GET_PRIVATE(self);
|
||||||
|
g_return_val_if_fail(FU_IS_TPM_DEVICE(self), NULL);
|
||||||
|
return priv->family;
|
||||||
}
|
}
|
||||||
|
|
||||||
static gboolean
|
void
|
||||||
fu_tpm_device_probe(FuDevice *device, GError **error)
|
fu_tpm_device_add_checksum(FuTpmDevice *self, guint idx, const gchar *checksum)
|
||||||
{
|
{
|
||||||
/* FuUdevDevice->probe */
|
FuTpmDevicePrivate *priv = GET_PRIVATE(self);
|
||||||
if (!FU_DEVICE_CLASS(fu_tpm_device_parent_class)->probe(device, error))
|
FuTpmDevicePcrItem *item = g_new0(FuTpmDevicePcrItem, 1);
|
||||||
return FALSE;
|
|
||||||
return fu_udev_device_set_physical_id(FU_UDEV_DEVICE(device), "tpm", error);
|
g_return_if_fail(FU_IS_TPM_DEVICE(self));
|
||||||
|
g_return_if_fail(checksum != NULL);
|
||||||
|
|
||||||
|
item->idx = idx;
|
||||||
|
item->checksum = g_strdup(checksum);
|
||||||
|
g_debug("added PCR-%02u=%s", item->idx, item->checksum);
|
||||||
|
g_ptr_array_add(priv->items, item);
|
||||||
}
|
}
|
||||||
|
|
||||||
static gboolean
|
GPtrArray *
|
||||||
fu_tpm_device_get_uint32(ESYS_CONTEXT *ctx, guint32 query, guint32 *val, GError **error)
|
fu_tpm_device_get_checksums(FuTpmDevice *self, guint idx)
|
||||||
{
|
{
|
||||||
TSS2_RC rc;
|
FuTpmDevicePrivate *priv = GET_PRIVATE(self);
|
||||||
g_autofree TPMS_CAPABILITY_DATA *capability = NULL;
|
g_autoptr(GPtrArray) array = g_ptr_array_new_with_free_func(g_free);
|
||||||
|
|
||||||
g_return_val_if_fail(val != NULL, FALSE);
|
g_return_val_if_fail(FU_IS_TPM_DEVICE(self), NULL);
|
||||||
|
|
||||||
rc = Esys_GetCapability(ctx,
|
for (guint i = 0; i < priv->items->len; i++) {
|
||||||
ESYS_TR_NONE,
|
FuTpmDevicePcrItem *item = g_ptr_array_index(priv->items, i);
|
||||||
ESYS_TR_NONE,
|
if (item->idx == idx)
|
||||||
ESYS_TR_NONE,
|
g_ptr_array_add(array, g_strdup(item->checksum));
|
||||||
TPM2_CAP_TPM_PROPERTIES,
|
|
||||||
query,
|
|
||||||
1,
|
|
||||||
NULL,
|
|
||||||
&capability);
|
|
||||||
if (rc != TSS2_RC_SUCCESS) {
|
|
||||||
g_set_error(error,
|
|
||||||
G_IO_ERROR,
|
|
||||||
G_IO_ERROR_NOT_SUPPORTED,
|
|
||||||
"capability request failed for query %x",
|
|
||||||
query);
|
|
||||||
return FALSE;
|
|
||||||
}
|
}
|
||||||
if (capability->data.tpmProperties.count == 0) {
|
return g_steal_pointer(&array);
|
||||||
g_set_error(error,
|
|
||||||
G_IO_ERROR,
|
|
||||||
G_IO_ERROR_NOT_SUPPORTED,
|
|
||||||
"no properties returned for query %x",
|
|
||||||
query);
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
if (capability->data.tpmProperties.tpmProperty[0].property != query) {
|
|
||||||
g_set_error(error,
|
|
||||||
G_IO_ERROR,
|
|
||||||
G_IO_ERROR_NOT_SUPPORTED,
|
|
||||||
"wrong query returned (got %x expected %x)",
|
|
||||||
capability->data.tpmProperties.tpmProperty[0].property,
|
|
||||||
query);
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
*val = capability->data.tpmProperties.tpmProperty[0].value;
|
|
||||||
return TRUE;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static gchar *
|
static void
|
||||||
fu_tpm_device_get_string(ESYS_CONTEXT *ctx, guint32 query, GError **error)
|
fu_tpm_device_to_string(FuDevice *device, guint idt, GString *str)
|
||||||
{
|
|
||||||
guint32 val_be = 0;
|
|
||||||
guint32 val;
|
|
||||||
gchar result[5] = {'\0'};
|
|
||||||
|
|
||||||
/* return four bytes */
|
|
||||||
if (!fu_tpm_device_get_uint32(ctx, query, &val_be, error))
|
|
||||||
return NULL;
|
|
||||||
val = GUINT32_FROM_BE(val_be);
|
|
||||||
memcpy(result, (gchar *)&val, 4);
|
|
||||||
|
|
||||||
/* convert non-ASCII into spaces */
|
|
||||||
for (guint i = 0; i < 4; i++) {
|
|
||||||
if (!g_ascii_isgraph(result[i]))
|
|
||||||
result[i] = 0x20;
|
|
||||||
}
|
|
||||||
|
|
||||||
return fu_common_strstrip(result);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* taken from TCG-TPM-Vendor-ID-Registry-Version-1.01-Revision-1.00.pdf */
|
|
||||||
static const gchar *
|
|
||||||
fu_tpm_device_convert_manufacturer(const gchar *manufacturer)
|
|
||||||
{
|
|
||||||
if (g_strcmp0(manufacturer, "AMD") == 0)
|
|
||||||
return "AMD";
|
|
||||||
if (g_strcmp0(manufacturer, "ATML") == 0)
|
|
||||||
return "Atmel";
|
|
||||||
if (g_strcmp0(manufacturer, "BRCM") == 0)
|
|
||||||
return "Broadcom";
|
|
||||||
if (g_strcmp0(manufacturer, "HPE") == 0)
|
|
||||||
return "HPE";
|
|
||||||
if (g_strcmp0(manufacturer, "IBM") == 0)
|
|
||||||
return "IBM";
|
|
||||||
if (g_strcmp0(manufacturer, "IFX") == 0)
|
|
||||||
return "Infineon";
|
|
||||||
if (g_strcmp0(manufacturer, "INTC") == 0)
|
|
||||||
return "Intel";
|
|
||||||
if (g_strcmp0(manufacturer, "LEN") == 0)
|
|
||||||
return "Lenovo";
|
|
||||||
if (g_strcmp0(manufacturer, "MSFT") == 0)
|
|
||||||
return "Microsoft";
|
|
||||||
if (g_strcmp0(manufacturer, "NSM") == 0)
|
|
||||||
return "National Semiconductor";
|
|
||||||
if (g_strcmp0(manufacturer, "NTZ") == 0)
|
|
||||||
return "Nationz";
|
|
||||||
if (g_strcmp0(manufacturer, "NTC") == 0)
|
|
||||||
return "Nuvoton Technology";
|
|
||||||
if (g_strcmp0(manufacturer, "QCOM") == 0)
|
|
||||||
return "Qualcomm";
|
|
||||||
if (g_strcmp0(manufacturer, "SMSC") == 0)
|
|
||||||
return "SMSC";
|
|
||||||
if (g_strcmp0(manufacturer, "STM") == 0)
|
|
||||||
return "ST Microelectronics";
|
|
||||||
if (g_strcmp0(manufacturer, "SMSN") == 0)
|
|
||||||
return "Samsung";
|
|
||||||
if (g_strcmp0(manufacturer, "SNS") == 0)
|
|
||||||
return "Sinosun";
|
|
||||||
if (g_strcmp0(manufacturer, "TXN") == 0)
|
|
||||||
return "Texas Instruments";
|
|
||||||
if (g_strcmp0(manufacturer, "WEC") == 0)
|
|
||||||
return "Winbond";
|
|
||||||
if (g_strcmp0(manufacturer, "ROCC") == 0)
|
|
||||||
return "Fuzhou Rockchip";
|
|
||||||
if (g_strcmp0(manufacturer, "GOOG") == 0)
|
|
||||||
return "Google";
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
static gboolean
|
|
||||||
fu_tpm_device_setup(FuDevice *device, GError **error)
|
|
||||||
{
|
{
|
||||||
FuTpmDevice *self = FU_TPM_DEVICE(device);
|
FuTpmDevice *self = FU_TPM_DEVICE(device);
|
||||||
FwupdVersionFormat verfmt;
|
FuTpmDevicePrivate *priv = GET_PRIVATE(self);
|
||||||
TSS2_RC rc;
|
if (priv->family != NULL)
|
||||||
const gchar *tmp;
|
fu_common_string_append_kv(str, idt, "Family", priv->family);
|
||||||
guint32 tpm_type = 0;
|
}
|
||||||
guint32 version1 = 0;
|
|
||||||
guint32 version2 = 0;
|
|
||||||
guint64 version_raw;
|
|
||||||
g_autofree gchar *id1 = NULL;
|
|
||||||
g_autofree gchar *id2 = NULL;
|
|
||||||
g_autofree gchar *id3 = NULL;
|
|
||||||
g_autofree gchar *id4 = NULL;
|
|
||||||
g_autofree gchar *manufacturer = NULL;
|
|
||||||
g_autofree gchar *model1 = NULL;
|
|
||||||
g_autofree gchar *model2 = NULL;
|
|
||||||
g_autofree gchar *model3 = NULL;
|
|
||||||
g_autofree gchar *model4 = NULL;
|
|
||||||
g_autofree gchar *model = NULL;
|
|
||||||
g_autofree gchar *vendor_id = NULL;
|
|
||||||
g_autofree gchar *version = NULL;
|
|
||||||
g_autoptr(ESYS_CONTEXT) ctx = NULL;
|
|
||||||
|
|
||||||
/* setup TSS */
|
static void
|
||||||
rc = Esys_Initialize(&ctx, NULL, NULL);
|
fu_tpm_v2_device_item_free(FuTpmDevicePcrItem *item)
|
||||||
if (rc != TSS2_RC_SUCCESS) {
|
{
|
||||||
g_set_error_literal(error,
|
g_free(item->checksum);
|
||||||
FWUPD_ERROR,
|
g_free(item);
|
||||||
FWUPD_ERROR_NOT_FOUND,
|
}
|
||||||
"failed to initialize TPM library");
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
rc = Esys_Startup(ctx, TPM2_SU_CLEAR);
|
|
||||||
if (rc != TSS2_RC_SUCCESS) {
|
|
||||||
g_set_error_literal(error,
|
|
||||||
G_IO_ERROR,
|
|
||||||
G_IO_ERROR_NOT_SUPPORTED,
|
|
||||||
"failed to initialize TPM");
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* lookup guaranteed details from TPM */
|
static void
|
||||||
self->family = fu_tpm_device_get_string(ctx, TPM2_PT_FAMILY_INDICATOR, error);
|
fu_tpm_device_finalize(GObject *object)
|
||||||
if (self->family == NULL) {
|
{
|
||||||
g_prefix_error(error, "failed to read TPM family: ");
|
FuTpmDevice *self = FU_TPM_DEVICE(object);
|
||||||
return FALSE;
|
FuTpmDevicePrivate *priv = GET_PRIVATE(self);
|
||||||
}
|
|
||||||
manufacturer = fu_tpm_device_get_string(ctx, TPM2_PT_MANUFACTURER, error);
|
|
||||||
if (manufacturer == NULL) {
|
|
||||||
g_prefix_error(error, "failed to read TPM manufacturer: ");
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
model1 = fu_tpm_device_get_string(ctx, TPM2_PT_VENDOR_STRING_1, error);
|
|
||||||
if (model1 == NULL) {
|
|
||||||
g_prefix_error(error, "failed to read TPM vendor string: ");
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
if (!fu_tpm_device_get_uint32(ctx, TPM2_PT_VENDOR_TPM_TYPE, &tpm_type, error)) {
|
|
||||||
g_prefix_error(error, "failed to read TPM type: ");
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* these are not guaranteed by spec and may be NULL */
|
g_free(priv->family);
|
||||||
model2 = fu_tpm_device_get_string(ctx, TPM2_PT_VENDOR_STRING_2, error);
|
g_ptr_array_unref(priv->items);
|
||||||
model3 = fu_tpm_device_get_string(ctx, TPM2_PT_VENDOR_STRING_3, error);
|
|
||||||
model4 = fu_tpm_device_get_string(ctx, TPM2_PT_VENDOR_STRING_4, error);
|
|
||||||
model = g_strjoin("", model1, model2, model3, model4, NULL);
|
|
||||||
|
|
||||||
/* add GUIDs to daemon */
|
G_OBJECT_CLASS(fu_tpm_device_parent_class)->finalize(object);
|
||||||
id1 = g_strdup_printf("TPM\\VEN_%s&DEV_%04X", manufacturer, tpm_type);
|
|
||||||
fu_device_add_instance_id(device, id1);
|
|
||||||
id2 = g_strdup_printf("TPM\\VEN_%s&MOD_%s", manufacturer, model);
|
|
||||||
fu_device_add_instance_id(device, id2);
|
|
||||||
id3 = g_strdup_printf("TPM\\VEN_%s&DEV_%04X&VER_%s", manufacturer, tpm_type, self->family);
|
|
||||||
fu_device_add_instance_id(device, id3);
|
|
||||||
id4 = g_strdup_printf("TPM\\VEN_%s&MOD_%s&VER_%s", manufacturer, model, self->family);
|
|
||||||
fu_device_add_instance_id(device, id4);
|
|
||||||
|
|
||||||
/* enforce vendors can only ship updates for their own hardware */
|
|
||||||
vendor_id = g_strdup_printf("TPM:%s", manufacturer);
|
|
||||||
fu_device_add_vendor_id(device, vendor_id);
|
|
||||||
tmp = fu_tpm_device_convert_manufacturer(manufacturer);
|
|
||||||
fu_device_set_vendor(device, tmp != NULL ? tmp : manufacturer);
|
|
||||||
|
|
||||||
/* get version */
|
|
||||||
if (!fu_tpm_device_get_uint32(ctx, TPM2_PT_FIRMWARE_VERSION_1, &version1, error))
|
|
||||||
return FALSE;
|
|
||||||
if (!fu_tpm_device_get_uint32(ctx, TPM2_PT_FIRMWARE_VERSION_2, &version2, error))
|
|
||||||
return FALSE;
|
|
||||||
version_raw = ((guint64)version1) << 32 | ((guint64)version2);
|
|
||||||
fu_device_set_version_raw(device, version_raw);
|
|
||||||
|
|
||||||
/* this has to be done after _add_instance_id() sets the quirks */
|
|
||||||
verfmt = fu_device_get_version_format(device);
|
|
||||||
version = fu_common_version_from_uint64(version_raw, verfmt);
|
|
||||||
fu_device_set_version_format(device, verfmt);
|
|
||||||
fu_device_set_version(device, version);
|
|
||||||
|
|
||||||
/* success */
|
|
||||||
return TRUE;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
fu_tpm_device_init(FuTpmDevice *self)
|
fu_tpm_device_init(FuTpmDevice *self)
|
||||||
{
|
{
|
||||||
|
FuTpmDevicePrivate *priv = GET_PRIVATE(self);
|
||||||
|
priv->items = g_ptr_array_new_with_free_func((GDestroyNotify)fu_tpm_v2_device_item_free);
|
||||||
fu_device_set_name(FU_DEVICE(self), "TPM");
|
fu_device_set_name(FU_DEVICE(self), "TPM");
|
||||||
fu_device_set_version_format(FU_DEVICE(self), FWUPD_VERSION_FORMAT_QUAD);
|
fu_device_set_version_format(FU_DEVICE(self), FWUPD_VERSION_FORMAT_QUAD);
|
||||||
fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_INTERNAL);
|
fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_INTERNAL);
|
||||||
@ -274,20 +112,12 @@ fu_tpm_device_init(FuTpmDevice *self)
|
|||||||
FU_DEVICE_INSTANCE_FLAG_NO_QUIRKS);
|
FU_DEVICE_INSTANCE_FLAG_NO_QUIRKS);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
|
||||||
fu_tpm_device_finalize(GObject *object)
|
|
||||||
{
|
|
||||||
FuTpmDevice *self = FU_TPM_DEVICE(object);
|
|
||||||
g_free(self->family);
|
|
||||||
G_OBJECT_CLASS(fu_tpm_device_parent_class)->finalize(object);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
fu_tpm_device_class_init(FuTpmDeviceClass *klass)
|
fu_tpm_device_class_init(FuTpmDeviceClass *klass)
|
||||||
{
|
{
|
||||||
GObjectClass *object_class = G_OBJECT_CLASS(klass);
|
GObjectClass *object_class = G_OBJECT_CLASS(klass);
|
||||||
FuDeviceClass *klass_device = FU_DEVICE_CLASS(klass);
|
FuDeviceClass *klass_device = FU_DEVICE_CLASS(klass);
|
||||||
|
|
||||||
object_class->finalize = fu_tpm_device_finalize;
|
object_class->finalize = fu_tpm_device_finalize;
|
||||||
klass_device->setup = fu_tpm_device_setup;
|
klass_device->to_string = fu_tpm_device_to_string;
|
||||||
klass_device->probe = fu_tpm_device_probe;
|
|
||||||
}
|
}
|
||||||
|
@ -9,7 +9,18 @@
|
|||||||
#include <fwupdplugin.h>
|
#include <fwupdplugin.h>
|
||||||
|
|
||||||
#define FU_TYPE_TPM_DEVICE (fu_tpm_device_get_type())
|
#define FU_TYPE_TPM_DEVICE (fu_tpm_device_get_type())
|
||||||
G_DECLARE_FINAL_TYPE(FuTpmDevice, fu_tpm_device, FU, TPM_DEVICE, FuUdevDevice)
|
G_DECLARE_DERIVABLE_TYPE(FuTpmDevice, fu_tpm_device, FU, TPM_DEVICE, FuUdevDevice)
|
||||||
|
|
||||||
|
struct _FuTpmDeviceClass {
|
||||||
|
FuDeviceClass parent_class;
|
||||||
|
gpointer __reserved[31];
|
||||||
|
};
|
||||||
|
|
||||||
|
void
|
||||||
|
fu_tpm_device_set_family(FuTpmDevice *self, const gchar *family);
|
||||||
const gchar *
|
const gchar *
|
||||||
fu_tpm_device_get_family(FuTpmDevice *self);
|
fu_tpm_device_get_family(FuTpmDevice *self);
|
||||||
|
void
|
||||||
|
fu_tpm_device_add_checksum(FuTpmDevice *self, guint idx, const gchar *checksum);
|
||||||
|
GPtrArray *
|
||||||
|
fu_tpm_device_get_checksums(FuTpmDevice *self, guint idx);
|
||||||
|
107
plugins/tpm/fu-tpm-v1-device.c
Normal file
107
plugins/tpm/fu-tpm-v1-device.c
Normal file
@ -0,0 +1,107 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2018 Richard Hughes <richard@hughsie.com>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: LGPL-2.1+
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
#include "fu-tpm-v1-device.h"
|
||||||
|
|
||||||
|
struct _FuTpmV1Device {
|
||||||
|
FuTpmDevice parent_instance;
|
||||||
|
};
|
||||||
|
|
||||||
|
G_DEFINE_TYPE(FuTpmV1Device, fu_tpm_v1_device, FU_TYPE_TPM_DEVICE)
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
_g_string_isxdigit(GString *str)
|
||||||
|
{
|
||||||
|
for (gsize i = 0; i < str->len; i++) {
|
||||||
|
if (!g_ascii_isxdigit(str->str[i]))
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
fu_tpm_device_parse_line(const gchar *line, gpointer user_data)
|
||||||
|
{
|
||||||
|
FuTpmDevice *self = FU_TPM_DEVICE(user_data);
|
||||||
|
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, ":", -1);
|
||||||
|
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]);
|
||||||
|
fu_common_string_replace(str, " ", "");
|
||||||
|
if ((str->len != 40 && str->len != 64) || !_g_string_isxdigit(str)) {
|
||||||
|
g_debug("not SHA-1 or SHA-256, skipping: %s", split[1]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
g_string_ascii_down(str);
|
||||||
|
fu_tpm_device_add_checksum(self, idx, str->str);
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
fu_tpm_v1_device_probe(FuDevice *device, GError **error)
|
||||||
|
{
|
||||||
|
FuTpmV1Device *self = FU_TPM_V1_DEVICE(device);
|
||||||
|
g_auto(GStrv) lines = NULL;
|
||||||
|
g_autofree gchar *buf_pcrs = NULL;
|
||||||
|
|
||||||
|
/* FuUdevDevice->probe */
|
||||||
|
if (!FU_DEVICE_CLASS(fu_tpm_v1_device_parent_class)->probe(device, error))
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
/* get entire contents */
|
||||||
|
if (!g_file_get_contents(fu_udev_device_get_device_file(FU_UDEV_DEVICE(device)),
|
||||||
|
&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_tpm_device_parse_line(lines[i] + 4, self);
|
||||||
|
}
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
fu_tpm_v1_device_init(FuTpmV1Device *self)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
fu_tpm_v1_device_class_init(FuTpmV1DeviceClass *klass)
|
||||||
|
{
|
||||||
|
FuDeviceClass *klass_device = FU_DEVICE_CLASS(klass);
|
||||||
|
klass_device->probe = fu_tpm_v1_device_probe;
|
||||||
|
}
|
||||||
|
|
||||||
|
FuTpmDevice *
|
||||||
|
fu_tpm_v1_device_new(FuContext *ctx)
|
||||||
|
{
|
||||||
|
return FU_TPM_DEVICE(g_object_new(FU_TYPE_TPM_V1_DEVICE, "context", ctx, NULL));
|
||||||
|
}
|
17
plugins/tpm/fu-tpm-v1-device.h
Normal file
17
plugins/tpm/fu-tpm-v1-device.h
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2018 Richard Hughes <richard@hughsie.com>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: LGPL-2.1+
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <fwupdplugin.h>
|
||||||
|
|
||||||
|
#include "fu-tpm-device.h"
|
||||||
|
|
||||||
|
#define FU_TYPE_TPM_V1_DEVICE (fu_tpm_v1_device_get_type())
|
||||||
|
G_DECLARE_FINAL_TYPE(FuTpmV1Device, fu_tpm_v1_device, FU, TPM_V1_DEVICE, FuTpmDevice)
|
||||||
|
|
||||||
|
FuTpmDevice *
|
||||||
|
fu_tpm_v1_device_new(FuContext *ctx);
|
357
plugins/tpm/fu-tpm-v2-device.c
Normal file
357
plugins/tpm/fu-tpm-v2-device.c
Normal file
@ -0,0 +1,357 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2018 Richard Hughes <richard@hughsie.com>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: LGPL-2.1+
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
#include <tss2/tss2_esys.h>
|
||||||
|
|
||||||
|
#include "fu-tpm-v2-device.h"
|
||||||
|
|
||||||
|
struct _FuTpmV2Device {
|
||||||
|
FuTpmDevice parent_instance;
|
||||||
|
};
|
||||||
|
|
||||||
|
G_DEFINE_TYPE(FuTpmV2Device, fu_tpm_v2_device, FU_TYPE_TPM_DEVICE)
|
||||||
|
|
||||||
|
static void
|
||||||
|
Esys_Finalize_autoptr_cleanup(ESYS_CONTEXT *esys_context)
|
||||||
|
{
|
||||||
|
Esys_Finalize(&esys_context);
|
||||||
|
}
|
||||||
|
G_DEFINE_AUTOPTR_CLEANUP_FUNC(ESYS_CONTEXT, Esys_Finalize_autoptr_cleanup)
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
fu_tpm_v2_device_probe(FuDevice *device, GError **error)
|
||||||
|
{
|
||||||
|
/* FuUdevDevice->probe */
|
||||||
|
if (!FU_DEVICE_CLASS(fu_tpm_v2_device_parent_class)->probe(device, error))
|
||||||
|
return FALSE;
|
||||||
|
return fu_udev_device_set_physical_id(FU_UDEV_DEVICE(device), "tpm", error);
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
fu_tpm_v2_device_get_uint32(ESYS_CONTEXT *ctx, guint32 query, guint32 *val, GError **error)
|
||||||
|
{
|
||||||
|
TSS2_RC rc;
|
||||||
|
g_autofree TPMS_CAPABILITY_DATA *capability = NULL;
|
||||||
|
|
||||||
|
g_return_val_if_fail(val != NULL, FALSE);
|
||||||
|
|
||||||
|
rc = Esys_GetCapability(ctx,
|
||||||
|
ESYS_TR_NONE,
|
||||||
|
ESYS_TR_NONE,
|
||||||
|
ESYS_TR_NONE,
|
||||||
|
TPM2_CAP_TPM_PROPERTIES,
|
||||||
|
query,
|
||||||
|
1,
|
||||||
|
NULL,
|
||||||
|
&capability);
|
||||||
|
if (rc != TSS2_RC_SUCCESS) {
|
||||||
|
g_set_error(error,
|
||||||
|
G_IO_ERROR,
|
||||||
|
G_IO_ERROR_NOT_SUPPORTED,
|
||||||
|
"capability request failed for query %x",
|
||||||
|
query);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
if (capability->data.tpmProperties.count == 0) {
|
||||||
|
g_set_error(error,
|
||||||
|
G_IO_ERROR,
|
||||||
|
G_IO_ERROR_NOT_SUPPORTED,
|
||||||
|
"no properties returned for query %x",
|
||||||
|
query);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
if (capability->data.tpmProperties.tpmProperty[0].property != query) {
|
||||||
|
g_set_error(error,
|
||||||
|
G_IO_ERROR,
|
||||||
|
G_IO_ERROR_NOT_SUPPORTED,
|
||||||
|
"wrong query returned (got %x expected %x)",
|
||||||
|
capability->data.tpmProperties.tpmProperty[0].property,
|
||||||
|
query);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
*val = capability->data.tpmProperties.tpmProperty[0].value;
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gchar *
|
||||||
|
fu_tpm_v2_device_get_string(ESYS_CONTEXT *ctx, guint32 query, GError **error)
|
||||||
|
{
|
||||||
|
guint32 val_be = 0;
|
||||||
|
guint32 val;
|
||||||
|
gchar result[5] = {'\0'};
|
||||||
|
|
||||||
|
/* return four bytes */
|
||||||
|
if (!fu_tpm_v2_device_get_uint32(ctx, query, &val_be, error))
|
||||||
|
return NULL;
|
||||||
|
val = GUINT32_FROM_BE(val_be);
|
||||||
|
memcpy(result, (gchar *)&val, 4);
|
||||||
|
|
||||||
|
/* convert non-ASCII into spaces */
|
||||||
|
for (guint i = 0; i < 4; i++) {
|
||||||
|
if (!g_ascii_isgraph(result[i]))
|
||||||
|
result[i] = 0x20;
|
||||||
|
}
|
||||||
|
|
||||||
|
return fu_common_strstrip(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* taken from TCG-TPM-Vendor-ID-Registry-Version-1.01-Revision-1.00.pdf */
|
||||||
|
static const gchar *
|
||||||
|
fu_tpm_v2_device_convert_manufacturer(const gchar *manufacturer)
|
||||||
|
{
|
||||||
|
if (g_strcmp0(manufacturer, "AMD") == 0)
|
||||||
|
return "AMD";
|
||||||
|
if (g_strcmp0(manufacturer, "ATML") == 0)
|
||||||
|
return "Atmel";
|
||||||
|
if (g_strcmp0(manufacturer, "BRCM") == 0)
|
||||||
|
return "Broadcom";
|
||||||
|
if (g_strcmp0(manufacturer, "HPE") == 0)
|
||||||
|
return "HPE";
|
||||||
|
if (g_strcmp0(manufacturer, "IBM") == 0)
|
||||||
|
return "IBM";
|
||||||
|
if (g_strcmp0(manufacturer, "IFX") == 0)
|
||||||
|
return "Infineon";
|
||||||
|
if (g_strcmp0(manufacturer, "INTC") == 0)
|
||||||
|
return "Intel";
|
||||||
|
if (g_strcmp0(manufacturer, "LEN") == 0)
|
||||||
|
return "Lenovo";
|
||||||
|
if (g_strcmp0(manufacturer, "MSFT") == 0)
|
||||||
|
return "Microsoft";
|
||||||
|
if (g_strcmp0(manufacturer, "NSM") == 0)
|
||||||
|
return "National Semiconductor";
|
||||||
|
if (g_strcmp0(manufacturer, "NTZ") == 0)
|
||||||
|
return "Nationz";
|
||||||
|
if (g_strcmp0(manufacturer, "NTC") == 0)
|
||||||
|
return "Nuvoton Technology";
|
||||||
|
if (g_strcmp0(manufacturer, "QCOM") == 0)
|
||||||
|
return "Qualcomm";
|
||||||
|
if (g_strcmp0(manufacturer, "SMSC") == 0)
|
||||||
|
return "SMSC";
|
||||||
|
if (g_strcmp0(manufacturer, "STM") == 0)
|
||||||
|
return "ST Microelectronics";
|
||||||
|
if (g_strcmp0(manufacturer, "SMSN") == 0)
|
||||||
|
return "Samsung";
|
||||||
|
if (g_strcmp0(manufacturer, "SNS") == 0)
|
||||||
|
return "Sinosun";
|
||||||
|
if (g_strcmp0(manufacturer, "TXN") == 0)
|
||||||
|
return "Texas Instruments";
|
||||||
|
if (g_strcmp0(manufacturer, "WEC") == 0)
|
||||||
|
return "Winbond";
|
||||||
|
if (g_strcmp0(manufacturer, "ROCC") == 0)
|
||||||
|
return "Fuzhou Rockchip";
|
||||||
|
if (g_strcmp0(manufacturer, "GOOG") == 0)
|
||||||
|
return "Google";
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
fu_tpm_v2_device_setup_pcrs(FuTpmV2Device *self, ESYS_CONTEXT *ctx, GError **error)
|
||||||
|
{
|
||||||
|
TSS2_RC rc;
|
||||||
|
g_autofree TPMS_CAPABILITY_DATA *capability_data = NULL;
|
||||||
|
TPML_PCR_SELECTION pcr_selection_in = {
|
||||||
|
0,
|
||||||
|
};
|
||||||
|
g_autofree TPML_DIGEST *pcr_values = NULL;
|
||||||
|
|
||||||
|
/* get hash algorithms supported by the TPM */
|
||||||
|
rc = Esys_GetCapability(ctx,
|
||||||
|
ESYS_TR_NONE,
|
||||||
|
ESYS_TR_NONE,
|
||||||
|
ESYS_TR_NONE,
|
||||||
|
TPM2_CAP_PCRS,
|
||||||
|
0,
|
||||||
|
1,
|
||||||
|
NULL,
|
||||||
|
&capability_data);
|
||||||
|
if (rc != TSS2_RC_SUCCESS) {
|
||||||
|
g_set_error_literal(error,
|
||||||
|
G_IO_ERROR,
|
||||||
|
G_IO_ERROR_NOT_SUPPORTED,
|
||||||
|
"failed to get hash algorithms supported by TPM");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* fetch PCR 0 for every supported hash algorithm */
|
||||||
|
pcr_selection_in.count = capability_data->data.assignedPCR.count;
|
||||||
|
for (guint i = 0; i < pcr_selection_in.count; i++) {
|
||||||
|
pcr_selection_in.pcrSelections[i].hash =
|
||||||
|
capability_data->data.assignedPCR.pcrSelections[i].hash;
|
||||||
|
pcr_selection_in.pcrSelections[i].sizeofSelect =
|
||||||
|
capability_data->data.assignedPCR.pcrSelections[i].sizeofSelect;
|
||||||
|
pcr_selection_in.pcrSelections[i].pcrSelect[0] = 0b00000001;
|
||||||
|
}
|
||||||
|
|
||||||
|
rc = Esys_PCR_Read(ctx,
|
||||||
|
ESYS_TR_NONE,
|
||||||
|
ESYS_TR_NONE,
|
||||||
|
ESYS_TR_NONE,
|
||||||
|
&pcr_selection_in,
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
&pcr_values);
|
||||||
|
if (rc != TSS2_RC_SUCCESS) {
|
||||||
|
g_set_error_literal(error,
|
||||||
|
G_IO_ERROR,
|
||||||
|
G_IO_ERROR_NOT_SUPPORTED,
|
||||||
|
"failed to read PCR values from TPM");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (guint i = 0; i < pcr_values->count; i++) {
|
||||||
|
g_autoptr(GString) str = NULL;
|
||||||
|
gboolean valid = FALSE;
|
||||||
|
|
||||||
|
str = g_string_new(NULL);
|
||||||
|
for (guint j = 0; j < pcr_values->digests[i].size; j++) {
|
||||||
|
gint64 val = pcr_values->digests[i].buffer[j];
|
||||||
|
if (val > 0)
|
||||||
|
valid = TRUE;
|
||||||
|
g_string_append_printf(str, "%02x", pcr_values->digests[i].buffer[j]);
|
||||||
|
}
|
||||||
|
if (valid) {
|
||||||
|
/* constant PCR index 0, since we only read this single PCR */
|
||||||
|
fu_tpm_device_add_checksum(FU_TPM_DEVICE(self), 0, str->str);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* success */
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
fu_tpm_v2_device_setup(FuDevice *device, GError **error)
|
||||||
|
{
|
||||||
|
FuTpmV2Device *self = FU_TPM_V2_DEVICE(device);
|
||||||
|
FwupdVersionFormat verfmt;
|
||||||
|
TSS2_RC rc;
|
||||||
|
const gchar *tmp;
|
||||||
|
guint32 tpm_type = 0;
|
||||||
|
guint32 version1 = 0;
|
||||||
|
guint32 version2 = 0;
|
||||||
|
guint64 version_raw;
|
||||||
|
g_autofree gchar *id1 = NULL;
|
||||||
|
g_autofree gchar *id2 = NULL;
|
||||||
|
g_autofree gchar *id3 = NULL;
|
||||||
|
g_autofree gchar *id4 = NULL;
|
||||||
|
g_autofree gchar *manufacturer = NULL;
|
||||||
|
g_autofree gchar *model1 = NULL;
|
||||||
|
g_autofree gchar *model2 = NULL;
|
||||||
|
g_autofree gchar *model3 = NULL;
|
||||||
|
g_autofree gchar *model4 = NULL;
|
||||||
|
g_autofree gchar *model = NULL;
|
||||||
|
g_autofree gchar *vendor_id = NULL;
|
||||||
|
g_autofree gchar *version = NULL;
|
||||||
|
g_autofree gchar *family = NULL;
|
||||||
|
g_autoptr(ESYS_CONTEXT) ctx = NULL;
|
||||||
|
|
||||||
|
/* suppress warning messages about missing TCTI libraries for tpm2-tss <2.3 */
|
||||||
|
if (g_getenv("FWUPD_UEFI_VERBOSE") == NULL)
|
||||||
|
g_setenv("TSS2_LOG", "esys+error,tcti+none", FALSE);
|
||||||
|
|
||||||
|
/* setup TSS */
|
||||||
|
rc = Esys_Initialize(&ctx, NULL, NULL);
|
||||||
|
if (rc != TSS2_RC_SUCCESS) {
|
||||||
|
g_set_error_literal(error,
|
||||||
|
FWUPD_ERROR,
|
||||||
|
FWUPD_ERROR_NOT_FOUND,
|
||||||
|
"failed to initialize TPM library");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
rc = Esys_Startup(ctx, TPM2_SU_CLEAR);
|
||||||
|
if (rc != TSS2_RC_SUCCESS) {
|
||||||
|
g_set_error_literal(error,
|
||||||
|
G_IO_ERROR,
|
||||||
|
G_IO_ERROR_NOT_SUPPORTED,
|
||||||
|
"failed to initialize TPM");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* lookup guaranteed details from TPM */
|
||||||
|
family = fu_tpm_v2_device_get_string(ctx, TPM2_PT_FAMILY_INDICATOR, error);
|
||||||
|
if (family == NULL) {
|
||||||
|
g_prefix_error(error, "failed to read TPM family: ");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
fu_tpm_device_set_family(FU_TPM_DEVICE(self), family);
|
||||||
|
manufacturer = fu_tpm_v2_device_get_string(ctx, TPM2_PT_MANUFACTURER, error);
|
||||||
|
if (manufacturer == NULL) {
|
||||||
|
g_prefix_error(error, "failed to read TPM manufacturer: ");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
model1 = fu_tpm_v2_device_get_string(ctx, TPM2_PT_VENDOR_STRING_1, error);
|
||||||
|
if (model1 == NULL) {
|
||||||
|
g_prefix_error(error, "failed to read TPM vendor string: ");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
if (!fu_tpm_v2_device_get_uint32(ctx, TPM2_PT_VENDOR_TPM_TYPE, &tpm_type, error)) {
|
||||||
|
g_prefix_error(error, "failed to read TPM type: ");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* these are not guaranteed by spec and may be NULL */
|
||||||
|
model2 = fu_tpm_v2_device_get_string(ctx, TPM2_PT_VENDOR_STRING_2, error);
|
||||||
|
model3 = fu_tpm_v2_device_get_string(ctx, TPM2_PT_VENDOR_STRING_3, error);
|
||||||
|
model4 = fu_tpm_v2_device_get_string(ctx, TPM2_PT_VENDOR_STRING_4, error);
|
||||||
|
model = g_strjoin("", model1, model2, model3, model4, NULL);
|
||||||
|
|
||||||
|
/* add GUIDs to daemon */
|
||||||
|
id1 = g_strdup_printf("TPM\\VEN_%s&DEV_%04X", manufacturer, tpm_type);
|
||||||
|
fu_device_add_instance_id(device, id1);
|
||||||
|
id2 = g_strdup_printf("TPM\\VEN_%s&MOD_%s", manufacturer, model);
|
||||||
|
fu_device_add_instance_id(device, id2);
|
||||||
|
id3 = g_strdup_printf("TPM\\VEN_%s&DEV_%04X&VER_%s", manufacturer, tpm_type, family);
|
||||||
|
fu_device_add_instance_id(device, id3);
|
||||||
|
id4 = g_strdup_printf("TPM\\VEN_%s&MOD_%s&VER_%s", manufacturer, model, family);
|
||||||
|
fu_device_add_instance_id(device, id4);
|
||||||
|
|
||||||
|
/* enforce vendors can only ship updates for their own hardware */
|
||||||
|
vendor_id = g_strdup_printf("TPM:%s", manufacturer);
|
||||||
|
fu_device_add_vendor_id(device, vendor_id);
|
||||||
|
tmp = fu_tpm_v2_device_convert_manufacturer(manufacturer);
|
||||||
|
fu_device_set_vendor(device, tmp != NULL ? tmp : manufacturer);
|
||||||
|
|
||||||
|
/* get version */
|
||||||
|
if (!fu_tpm_v2_device_get_uint32(ctx, TPM2_PT_FIRMWARE_VERSION_1, &version1, error))
|
||||||
|
return FALSE;
|
||||||
|
if (!fu_tpm_v2_device_get_uint32(ctx, TPM2_PT_FIRMWARE_VERSION_2, &version2, error))
|
||||||
|
return FALSE;
|
||||||
|
version_raw = ((guint64)version1) << 32 | ((guint64)version2);
|
||||||
|
fu_device_set_version_raw(device, version_raw);
|
||||||
|
|
||||||
|
/* this has to be done after _add_instance_id() sets the quirks */
|
||||||
|
verfmt = fu_device_get_version_format(device);
|
||||||
|
version = fu_common_version_from_uint64(version_raw, verfmt);
|
||||||
|
fu_device_set_version_format(device, verfmt);
|
||||||
|
fu_device_set_version(device, version);
|
||||||
|
|
||||||
|
/* get PCRs */
|
||||||
|
return fu_tpm_v2_device_setup_pcrs(self, ctx, error);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
fu_tpm_v2_device_init(FuTpmV2Device *self)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
fu_tpm_v2_device_class_init(FuTpmV2DeviceClass *klass)
|
||||||
|
{
|
||||||
|
FuDeviceClass *klass_device = FU_DEVICE_CLASS(klass);
|
||||||
|
klass_device->setup = fu_tpm_v2_device_setup;
|
||||||
|
klass_device->probe = fu_tpm_v2_device_probe;
|
||||||
|
}
|
||||||
|
|
||||||
|
FuTpmDevice *
|
||||||
|
fu_tpm_v2_device_new(FuContext *ctx)
|
||||||
|
{
|
||||||
|
FuTpmV2Device *self;
|
||||||
|
self = g_object_new(FU_TYPE_TPM_V2_DEVICE, "context", ctx, NULL);
|
||||||
|
return FU_TPM_DEVICE(self);
|
||||||
|
}
|
17
plugins/tpm/fu-tpm-v2-device.h
Normal file
17
plugins/tpm/fu-tpm-v2-device.h
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2018 Richard Hughes <richard@hughsie.com>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: LGPL-2.1+
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <fwupdplugin.h>
|
||||||
|
|
||||||
|
#include "fu-tpm-device.h"
|
||||||
|
|
||||||
|
#define FU_TYPE_TPM_V2_DEVICE (fu_tpm_v2_device_get_type())
|
||||||
|
G_DECLARE_FINAL_TYPE(FuTpmV2Device, fu_tpm_v2_device, FU, TPM_V2_DEVICE, FuTpmDevice)
|
||||||
|
|
||||||
|
FuTpmDevice *
|
||||||
|
fu_tpm_v2_device_new(FuContext *ctx);
|
@ -15,6 +15,8 @@ shared_module('fu_plugin_tpm',
|
|||||||
sources : [
|
sources : [
|
||||||
'fu-plugin-tpm.c',
|
'fu-plugin-tpm.c',
|
||||||
'fu-tpm-device.c',
|
'fu-tpm-device.c',
|
||||||
|
'fu-tpm-v1-device.c',
|
||||||
|
'fu-tpm-v2-device.c',
|
||||||
],
|
],
|
||||||
include_directories : [
|
include_directories : [
|
||||||
root_incdir,
|
root_incdir,
|
||||||
@ -33,4 +35,36 @@ shared_module('fu_plugin_tpm',
|
|||||||
tpm2tss,
|
tpm2tss,
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if get_option('tests')
|
||||||
|
env = environment()
|
||||||
|
env.set('G_TEST_SRCDIR', meson.current_source_dir())
|
||||||
|
env.set('G_TEST_BUILDDIR', meson.current_build_dir())
|
||||||
|
env.set('FWUPD_LOCALSTATEDIR', '/tmp/fwupd-self-test/var')
|
||||||
|
e = executable(
|
||||||
|
'tpm-self-test',
|
||||||
|
fu_hash,
|
||||||
|
sources : [
|
||||||
|
'fu-self-test.c',
|
||||||
|
'fu-tpm-device.c',
|
||||||
|
'fu-tpm-v1-device.c',
|
||||||
|
'fu-tpm-v2-device.c',
|
||||||
|
],
|
||||||
|
include_directories : [
|
||||||
|
root_incdir,
|
||||||
|
fwupd_incdir,
|
||||||
|
fwupdplugin_incdir,
|
||||||
|
],
|
||||||
|
dependencies : [
|
||||||
|
plugin_deps,
|
||||||
|
tpm2tss,
|
||||||
|
],
|
||||||
|
link_with : [
|
||||||
|
fwupd,
|
||||||
|
fwupdplugin,
|
||||||
|
],
|
||||||
|
c_args : cargs
|
||||||
|
)
|
||||||
|
test('tpm-self-test', e, env: env)
|
||||||
|
endif
|
||||||
endif
|
endif
|
||||||
|
@ -14,73 +14,6 @@
|
|||||||
#include "fu-uefi-bgrt.h"
|
#include "fu-uefi-bgrt.h"
|
||||||
#include "fu-uefi-cod-device.h"
|
#include "fu-uefi-cod-device.h"
|
||||||
#include "fu-uefi-common.h"
|
#include "fu-uefi-common.h"
|
||||||
#include "fu-uefi-pcrs.h"
|
|
||||||
|
|
||||||
static void
|
|
||||||
fu_uefi_pcrs_1_2_func(void)
|
|
||||||
{
|
|
||||||
gboolean ret;
|
|
||||||
g_autoptr(FuUefiPcrs) pcrs = fu_uefi_pcrs_new();
|
|
||||||
g_autoptr(GError) error = NULL;
|
|
||||||
g_autoptr(GPtrArray) pcr0s = NULL;
|
|
||||||
g_autoptr(GPtrArray) pcrXs = NULL;
|
|
||||||
g_autofree gchar *testdatadir = NULL;
|
|
||||||
|
|
||||||
testdatadir = g_test_build_filename(G_TEST_DIST, "tests", NULL);
|
|
||||||
g_setenv("FWUPD_SYSFSTPMDIR", testdatadir, TRUE);
|
|
||||||
|
|
||||||
ret = fu_uefi_pcrs_setup(pcrs, &error);
|
|
||||||
g_assert_no_error(error);
|
|
||||||
g_assert_true(ret);
|
|
||||||
pcr0s = fu_uefi_pcrs_get_checksums(pcrs, 0);
|
|
||||||
g_assert_nonnull(pcr0s);
|
|
||||||
g_assert_cmpint(pcr0s->len, ==, 1);
|
|
||||||
pcrXs = fu_uefi_pcrs_get_checksums(pcrs, 999);
|
|
||||||
g_assert_nonnull(pcrXs);
|
|
||||||
g_assert_cmpint(pcrXs->len, ==, 0);
|
|
||||||
|
|
||||||
g_unsetenv("FWUPD_SYSFSTPMDIR");
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
fu_uefi_pcrs_2_0_func(void)
|
|
||||||
{
|
|
||||||
g_autoptr(FuUefiPcrs) pcrs = fu_uefi_pcrs_new();
|
|
||||||
g_autoptr(GError) error = NULL;
|
|
||||||
g_autoptr(GPtrArray) pcr0s = NULL;
|
|
||||||
g_autoptr(GPtrArray) pcrXs = NULL;
|
|
||||||
const gchar *tpm_server_running = g_getenv("TPM_SERVER_RUNNING");
|
|
||||||
g_setenv("FWUPD_FORCE_TPM2", "1", TRUE);
|
|
||||||
|
|
||||||
#ifndef HAVE_TSS2
|
|
||||||
g_test_skip("Compiled without TPM2.0 support");
|
|
||||||
return;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef HAVE_GETUID
|
|
||||||
if (tpm_server_running == NULL && (getuid() != 0 || geteuid() != 0)) {
|
|
||||||
g_test_skip("TPM2.0 tests require simulated TPM2.0 running or need root access "
|
|
||||||
"with physical TPM");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (!fu_uefi_pcrs_setup(pcrs, &error)) {
|
|
||||||
if (tpm_server_running == NULL &&
|
|
||||||
g_error_matches(error, FWUPD_ERROR, FWUPD_ERROR_NOT_FOUND)) {
|
|
||||||
g_test_skip("no physical or simulated TPM 2.0 device available");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
g_assert_no_error(error);
|
|
||||||
pcr0s = fu_uefi_pcrs_get_checksums(pcrs, 0);
|
|
||||||
g_assert_nonnull(pcr0s);
|
|
||||||
g_assert_cmpint(pcr0s->len, >=, 1);
|
|
||||||
pcrXs = fu_uefi_pcrs_get_checksums(pcrs, 999);
|
|
||||||
g_assert_nonnull(pcrXs);
|
|
||||||
g_assert_cmpint(pcrXs->len, ==, 0);
|
|
||||||
g_unsetenv("FWUPD_FORCE_TPM2");
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
fu_uefi_ucs2_func(void)
|
fu_uefi_ucs2_func(void)
|
||||||
@ -375,8 +308,6 @@ main(int argc, char **argv)
|
|||||||
g_setenv("G_MESSAGES_DEBUG", "all", TRUE);
|
g_setenv("G_MESSAGES_DEBUG", "all", TRUE);
|
||||||
|
|
||||||
/* tests go here */
|
/* tests go here */
|
||||||
g_test_add_func("/uefi/pcrs1.2", fu_uefi_pcrs_1_2_func);
|
|
||||||
g_test_add_func("/uefi/pcrs2.0", fu_uefi_pcrs_2_0_func);
|
|
||||||
g_test_add_func("/uefi/ucs2", fu_uefi_ucs2_func);
|
g_test_add_func("/uefi/ucs2", fu_uefi_ucs2_func);
|
||||||
g_test_add_func("/uefi/bgrt", fu_uefi_bgrt_func);
|
g_test_add_func("/uefi/bgrt", fu_uefi_bgrt_func);
|
||||||
g_test_add_func("/uefi/framebuffer", fu_uefi_framebuffer_func);
|
g_test_add_func("/uefi/framebuffer", fu_uefi_framebuffer_func);
|
||||||
|
@ -16,7 +16,6 @@
|
|||||||
#include "fu-uefi-common.h"
|
#include "fu-uefi-common.h"
|
||||||
#include "fu-uefi-device.h"
|
#include "fu-uefi-device.h"
|
||||||
#include "fu-uefi-devpath.h"
|
#include "fu-uefi-devpath.h"
|
||||||
#include "fu-uefi-pcrs.h"
|
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
FuVolume *esp;
|
FuVolume *esp;
|
||||||
@ -592,39 +591,6 @@ fu_uefi_device_cleanup(FuDevice *device, FwupdInstallFlags flags, GError **error
|
|||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
static gboolean
|
|
||||||
fu_uefi_device_add_system_checksum(FuDevice *device, GError **error)
|
|
||||||
{
|
|
||||||
g_autoptr(FuUefiPcrs) pcrs = fu_uefi_pcrs_new();
|
|
||||||
g_autoptr(GError) error_local = NULL;
|
|
||||||
g_autoptr(GPtrArray) pcr0s = NULL;
|
|
||||||
|
|
||||||
/* get all the PCRs */
|
|
||||||
if (!fu_uefi_pcrs_setup(pcrs, &error_local)) {
|
|
||||||
if (g_error_matches(error_local, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED) ||
|
|
||||||
g_error_matches(error_local, FWUPD_ERROR, FWUPD_ERROR_NOT_FOUND)) {
|
|
||||||
g_debug("%s", error_local->message);
|
|
||||||
return TRUE;
|
|
||||||
}
|
|
||||||
g_propagate_error(error, g_steal_pointer(&error_local));
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* get all the PCR0s */
|
|
||||||
pcr0s = fu_uefi_pcrs_get_checksums(pcrs, 0);
|
|
||||||
if (pcr0s->len == 0) {
|
|
||||||
g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, "no PCR0s detected");
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
for (guint i = 0; i < pcr0s->len; i++) {
|
|
||||||
const gchar *checksum = g_ptr_array_index(pcr0s, i);
|
|
||||||
fu_device_add_checksum(device, checksum);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* success */
|
|
||||||
return TRUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
static gboolean
|
static gboolean
|
||||||
fu_uefi_device_probe(FuDevice *device, GError **error)
|
fu_uefi_device_probe(FuDevice *device, GError **error)
|
||||||
{
|
{
|
||||||
@ -688,14 +654,6 @@ fu_uefi_device_probe(FuDevice *device, GError **error)
|
|||||||
fu_device_add_instance_id(device, "main-system-firmware");
|
fu_device_add_instance_id(device, "main-system-firmware");
|
||||||
}
|
}
|
||||||
|
|
||||||
/* set the PCR0 as the device checksum */
|
|
||||||
if (priv->kind == FU_UEFI_DEVICE_KIND_SYSTEM_FIRMWARE) {
|
|
||||||
g_autoptr(GError) error_local = NULL;
|
|
||||||
fu_device_add_flag(device, FWUPD_DEVICE_FLAG_CAN_VERIFY);
|
|
||||||
if (!fu_uefi_device_add_system_checksum(device, &error_local))
|
|
||||||
g_warning("Failed to get PCR0s: %s", error_local->message);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* whether to create a missing header */
|
/* whether to create a missing header */
|
||||||
if (priv->kind == FU_UEFI_DEVICE_KIND_FMP ||
|
if (priv->kind == FU_UEFI_DEVICE_KIND_FMP ||
|
||||||
priv->kind == FU_UEFI_DEVICE_KIND_DELL_TPM_FIRMWARE)
|
priv->kind == FU_UEFI_DEVICE_KIND_DELL_TPM_FIRMWARE)
|
||||||
|
@ -1,296 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2018 Richard Hughes <richard@hughsie.com>
|
|
||||||
*
|
|
||||||
* SPDX-License-Identifier: LGPL-2.1+
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "config.h"
|
|
||||||
|
|
||||||
#include <fwupdplugin.h>
|
|
||||||
#ifdef HAVE_TSS2
|
|
||||||
#include <tss2/tss2_esys.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#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)
|
|
||||||
|
|
||||||
#ifdef HAVE_TSS2
|
|
||||||
static void
|
|
||||||
Esys_Finalize_autoptr_cleanup(ESYS_CONTEXT *esys_context)
|
|
||||||
{
|
|
||||||
Esys_Finalize(&esys_context);
|
|
||||||
}
|
|
||||||
G_DEFINE_AUTOPTR_CLEANUP_FUNC(ESYS_CONTEXT, Esys_Finalize_autoptr_cleanup)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
static gboolean
|
|
||||||
_g_string_isxdigit(GString *str)
|
|
||||||
{
|
|
||||||
for (gsize i = 0; i < str->len; i++) {
|
|
||||||
if (!g_ascii_isxdigit(str->str[i]))
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
return TRUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
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, ":", -1);
|
|
||||||
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]);
|
|
||||||
fu_common_string_replace(str, " ", "");
|
|
||||||
if ((str->len != 40 && str->len != 64) || !_g_string_isxdigit(str)) {
|
|
||||||
g_debug("not SHA-1 or SHA-256, skipping: %s", split[1]);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
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_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, GError **error)
|
|
||||||
{
|
|
||||||
#ifdef HAVE_TSS2
|
|
||||||
TSS2_RC rc;
|
|
||||||
g_autoptr(ESYS_CONTEXT) ctx = NULL;
|
|
||||||
g_autofree TPMS_CAPABILITY_DATA *capability_data = NULL;
|
|
||||||
TPML_PCR_SELECTION pcr_selection_in = {
|
|
||||||
0,
|
|
||||||
};
|
|
||||||
g_autofree TPML_DIGEST *pcr_values = NULL;
|
|
||||||
|
|
||||||
/* suppress warning messages about missing TCTI libraries for tpm2-tss <2.3 */
|
|
||||||
if (g_getenv("FWUPD_UEFI_VERBOSE") == NULL) {
|
|
||||||
g_setenv("TSS2_LOG", "esys+error,tcti+none", FALSE);
|
|
||||||
}
|
|
||||||
|
|
||||||
rc = Esys_Initialize(&ctx, NULL, NULL);
|
|
||||||
if (rc != TSS2_RC_SUCCESS) {
|
|
||||||
g_set_error_literal(error,
|
|
||||||
FWUPD_ERROR,
|
|
||||||
FWUPD_ERROR_NOT_FOUND,
|
|
||||||
"failed to initialize TPM library");
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
rc = Esys_Startup(ctx, TPM2_SU_CLEAR);
|
|
||||||
if (rc != TSS2_RC_SUCCESS) {
|
|
||||||
g_set_error_literal(error,
|
|
||||||
G_IO_ERROR,
|
|
||||||
G_IO_ERROR_NOT_SUPPORTED,
|
|
||||||
"failed to initialize TPM");
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* get hash algorithms supported by the TPM */
|
|
||||||
rc = Esys_GetCapability(ctx,
|
|
||||||
ESYS_TR_NONE,
|
|
||||||
ESYS_TR_NONE,
|
|
||||||
ESYS_TR_NONE,
|
|
||||||
TPM2_CAP_PCRS,
|
|
||||||
0,
|
|
||||||
1,
|
|
||||||
NULL,
|
|
||||||
&capability_data);
|
|
||||||
if (rc != TSS2_RC_SUCCESS) {
|
|
||||||
g_set_error_literal(error,
|
|
||||||
G_IO_ERROR,
|
|
||||||
G_IO_ERROR_NOT_SUPPORTED,
|
|
||||||
"failed to get hash algorithms supported by TPM");
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* fetch PCR 0 for every supported hash algorithm */
|
|
||||||
pcr_selection_in.count = capability_data->data.assignedPCR.count;
|
|
||||||
for (guint i = 0; i < pcr_selection_in.count; i++) {
|
|
||||||
pcr_selection_in.pcrSelections[i].hash =
|
|
||||||
capability_data->data.assignedPCR.pcrSelections[i].hash;
|
|
||||||
pcr_selection_in.pcrSelections[i].sizeofSelect =
|
|
||||||
capability_data->data.assignedPCR.pcrSelections[i].sizeofSelect;
|
|
||||||
pcr_selection_in.pcrSelections[i].pcrSelect[0] = 0b00000001;
|
|
||||||
}
|
|
||||||
|
|
||||||
rc = Esys_PCR_Read(ctx,
|
|
||||||
ESYS_TR_NONE,
|
|
||||||
ESYS_TR_NONE,
|
|
||||||
ESYS_TR_NONE,
|
|
||||||
&pcr_selection_in,
|
|
||||||
NULL,
|
|
||||||
NULL,
|
|
||||||
&pcr_values);
|
|
||||||
if (rc != TSS2_RC_SUCCESS) {
|
|
||||||
g_set_error_literal(error,
|
|
||||||
G_IO_ERROR,
|
|
||||||
G_IO_ERROR_NOT_SUPPORTED,
|
|
||||||
"failed to read PCR values from TPM");
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (guint i = 0; i < pcr_values->count; i++) {
|
|
||||||
FuUefiPcrItem *item;
|
|
||||||
g_autoptr(GString) str = NULL;
|
|
||||||
gboolean valid = FALSE;
|
|
||||||
|
|
||||||
str = g_string_new(NULL);
|
|
||||||
for (guint j = 0; j < pcr_values->digests[i].size; j++) {
|
|
||||||
gint64 val = pcr_values->digests[i].buffer[j];
|
|
||||||
if (val > 0)
|
|
||||||
valid = TRUE;
|
|
||||||
g_string_append_printf(str, "%02x", pcr_values->digests[i].buffer[j]);
|
|
||||||
}
|
|
||||||
if (valid) {
|
|
||||||
item = g_new0(FuUefiPcrItem, 1);
|
|
||||||
item->idx =
|
|
||||||
0; /* constant PCR index 0, since we only read this single PCR */
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* success */
|
|
||||||
return TRUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
|
|
||||||
g_return_val_if_fail(FU_IS_UEFI_PCRS(self), FALSE);
|
|
||||||
|
|
||||||
/* look for TPM 1.2 */
|
|
||||||
sysfstpmdir = fu_common_get_path(FU_PATH_KIND_SYSFSDIR_TPM);
|
|
||||||
devpath = g_build_filename(sysfstpmdir, "tpm0", NULL);
|
|
||||||
fn_pcrs = g_build_filename(devpath, "pcrs", NULL);
|
|
||||||
if (g_file_test(fn_pcrs, G_FILE_TEST_EXISTS) && g_getenv("FWUPD_FORCE_TPM2") == NULL) {
|
|
||||||
if (!fu_uefi_pcrs_setup_tpm12(self, fn_pcrs, error))
|
|
||||||
return FALSE;
|
|
||||||
|
|
||||||
/* assume TPM 2.0 */
|
|
||||||
} else {
|
|
||||||
if (!fu_uefi_pcrs_setup_tpm20(self, 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);
|
|
||||||
}
|
|
@ -1,17 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2017 Richard Hughes <richard@hughsie.com>
|
|
||||||
*
|
|
||||||
* SPDX-License-Identifier: LGPL-2.1+
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#define FU_TYPE_UEFI_PCRS (fu_uefi_pcrs_get_type())
|
|
||||||
G_DECLARE_FINAL_TYPE(FuUefiPcrs, fu_uefi_pcrs, FU, UEFI_PCRS, GObject)
|
|
||||||
|
|
||||||
FuUefiPcrs *
|
|
||||||
fu_uefi_pcrs_new(void);
|
|
||||||
gboolean
|
|
||||||
fu_uefi_pcrs_setup(FuUefiPcrs *self, GError **error);
|
|
||||||
GPtrArray *
|
|
||||||
fu_uefi_pcrs_get_checksums(FuUefiPcrs *self, guint idx);
|
|
@ -42,7 +42,6 @@ shared_module('fu_plugin_uefi_capsule',
|
|||||||
'fu-uefi-grub-device.c',
|
'fu-uefi-grub-device.c',
|
||||||
'fu-uefi-device.c',
|
'fu-uefi-device.c',
|
||||||
'fu-uefi-devpath.c',
|
'fu-uefi-devpath.c',
|
||||||
'fu-uefi-pcrs.c',
|
|
||||||
'fu-uefi-update-info.c',
|
'fu-uefi-update-info.c',
|
||||||
backend_srcs,
|
backend_srcs,
|
||||||
],
|
],
|
||||||
@ -62,7 +61,6 @@ shared_module('fu_plugin_uefi_capsule',
|
|||||||
plugin_deps,
|
plugin_deps,
|
||||||
platform_deps,
|
platform_deps,
|
||||||
efiboot,
|
efiboot,
|
||||||
tpm2tss,
|
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -81,7 +79,6 @@ fwupdate = executable(
|
|||||||
'fu-uefi-nvram-device.c',
|
'fu-uefi-nvram-device.c',
|
||||||
'fu-uefi-grub-device.c',
|
'fu-uefi-grub-device.c',
|
||||||
'fu-uefi-devpath.c',
|
'fu-uefi-devpath.c',
|
||||||
'fu-uefi-pcrs.c',
|
|
||||||
'fu-uefi-update-info.c',
|
'fu-uefi-update-info.c',
|
||||||
backend_srcs,
|
backend_srcs,
|
||||||
],
|
],
|
||||||
@ -94,7 +91,6 @@ fwupdate = executable(
|
|||||||
plugin_deps,
|
plugin_deps,
|
||||||
platform_deps,
|
platform_deps,
|
||||||
efiboot,
|
efiboot,
|
||||||
tpm2tss,
|
|
||||||
],
|
],
|
||||||
link_with : [
|
link_with : [
|
||||||
fwupd,
|
fwupd,
|
||||||
@ -167,7 +163,6 @@ if get_option('tests')
|
|||||||
'fu-uefi-nvram-device.c',
|
'fu-uefi-nvram-device.c',
|
||||||
'fu-uefi-grub-device.c',
|
'fu-uefi-grub-device.c',
|
||||||
'fu-uefi-devpath.c',
|
'fu-uefi-devpath.c',
|
||||||
'fu-uefi-pcrs.c',
|
|
||||||
'fu-uefi-update-info.c',
|
'fu-uefi-update-info.c',
|
||||||
'fu-ucs2.c',
|
'fu-ucs2.c',
|
||||||
backend_srcs,
|
backend_srcs,
|
||||||
@ -181,7 +176,6 @@ if get_option('tests')
|
|||||||
plugin_deps,
|
plugin_deps,
|
||||||
platform_deps,
|
platform_deps,
|
||||||
efiboot,
|
efiboot,
|
||||||
tpm2tss,
|
|
||||||
],
|
],
|
||||||
link_with : [
|
link_with : [
|
||||||
fwupd,
|
fwupd,
|
||||||
|
Loading…
Reference in New Issue
Block a user