diff --git a/plugins/tpm/fu-plugin-tpm.c b/plugins/tpm/fu-plugin-tpm.c index 5f56e524f..943682443 100644 --- a/plugins/tpm/fu-plugin-tpm.c +++ b/plugins/tpm/fu-plugin-tpm.c @@ -8,10 +8,12 @@ #include -#include "fu-tpm-device.h" +#include "fu-tpm-v1-device.h" +#include "fu-tpm-v2-device.h" struct FuPluginData { - FuDevice *tpm_device; + FuTpmDevice *tpm_device; + FuDevice *bios_device; gboolean has_tpm_v20; }; @@ -21,7 +23,7 @@ fu_plugin_init(FuPlugin *plugin) fu_plugin_alloc_data(plugin, sizeof(FuPluginData)); fu_plugin_set_build_hash(plugin, FU_BUILD_HASH); 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 @@ -30,6 +32,42 @@ fu_plugin_destroy(FuPlugin *plugin) FuPluginData *data = fu_plugin_get_data(plugin); if (data->tpm_device != NULL) 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 @@ -38,10 +76,13 @@ fu_plugin_device_added(FuPlugin *plugin, FuDevice *dev) FuPluginData *data = fu_plugin_get_data(plugin); 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) data->has_tpm_v20 = TRUE; fu_plugin_add_report_metadata(plugin, "TpmFamily", family); + + /* ensure */ + fu_plugin_tpm_set_bios_pcr0s(plugin); } void @@ -67,7 +108,28 @@ fu_plugin_add_security_attrs(FuPlugin *plugin, FuSecurityAttrs *attrs) } /* 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_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; +} diff --git a/plugins/tpm/fu-self-test.c b/plugins/tpm/fu-self-test.c new file mode 100644 index 000000000..edcbd7355 --- /dev/null +++ b/plugins/tpm/fu-self-test.c @@ -0,0 +1,96 @@ +/* + * Copyright (C) 2018 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#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(); +} diff --git a/plugins/tpm/fu-tpm-device.c b/plugins/tpm/fu-tpm-device.c index f1038f03b..3a83c007a 100644 --- a/plugins/tpm/fu-tpm-device.c +++ b/plugins/tpm/fu-tpm-device.c @@ -6,264 +6,102 @@ #include "config.h" -#include - #include "fu-tpm-device.h" -struct _FuTpmDevice { - FuUdevDevice parent_instance; +typedef struct { + guint idx; + gchar *checksum; +} FuTpmDevicePcrItem; + +typedef struct { 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 -Esys_Finalize_autoptr_cleanup(ESYS_CONTEXT *esys_context) +#define GET_PRIVATE(o) (fu_tpm_device_get_instance_private(o)) + +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 * 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 -fu_tpm_device_probe(FuDevice *device, GError **error) +void +fu_tpm_device_add_checksum(FuTpmDevice *self, guint idx, const gchar *checksum) { - /* FuUdevDevice->probe */ - if (!FU_DEVICE_CLASS(fu_tpm_device_parent_class)->probe(device, error)) - return FALSE; - return fu_udev_device_set_physical_id(FU_UDEV_DEVICE(device), "tpm", error); + FuTpmDevicePrivate *priv = GET_PRIVATE(self); + FuTpmDevicePcrItem *item = g_new0(FuTpmDevicePcrItem, 1); + + 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 -fu_tpm_device_get_uint32(ESYS_CONTEXT *ctx, guint32 query, guint32 *val, GError **error) +GPtrArray * +fu_tpm_device_get_checksums(FuTpmDevice *self, guint idx) { - TSS2_RC rc; - g_autofree TPMS_CAPABILITY_DATA *capability = NULL; + FuTpmDevicePrivate *priv = GET_PRIVATE(self); + 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, - 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; + for (guint i = 0; i < priv->items->len; i++) { + FuTpmDevicePcrItem *item = g_ptr_array_index(priv->items, i); + if (item->idx == idx) + g_ptr_array_add(array, g_strdup(item->checksum)); } - 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; + return g_steal_pointer(&array); } -static gchar * -fu_tpm_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_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) +static void +fu_tpm_device_to_string(FuDevice *device, guint idt, GString *str) { FuTpmDevice *self = FU_TPM_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_autoptr(ESYS_CONTEXT) ctx = NULL; + FuTpmDevicePrivate *priv = GET_PRIVATE(self); + if (priv->family != NULL) + fu_common_string_append_kv(str, idt, "Family", priv->family); +} - /* 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; - } +static void +fu_tpm_v2_device_item_free(FuTpmDevicePcrItem *item) +{ + g_free(item->checksum); + g_free(item); +} - /* lookup guaranteed details from TPM */ - self->family = fu_tpm_device_get_string(ctx, TPM2_PT_FAMILY_INDICATOR, error); - if (self->family == NULL) { - g_prefix_error(error, "failed to read TPM family: "); - return FALSE; - } - 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; - } +static void +fu_tpm_device_finalize(GObject *object) +{ + FuTpmDevice *self = FU_TPM_DEVICE(object); + FuTpmDevicePrivate *priv = GET_PRIVATE(self); - /* these are not guaranteed by spec and may be NULL */ - model2 = fu_tpm_device_get_string(ctx, TPM2_PT_VENDOR_STRING_2, error); - 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); + g_free(priv->family); + g_ptr_array_unref(priv->items); - /* 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, 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; + G_OBJECT_CLASS(fu_tpm_device_parent_class)->finalize(object); } static void 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_version_format(FU_DEVICE(self), FWUPD_VERSION_FORMAT_QUAD); 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); } -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 fu_tpm_device_class_init(FuTpmDeviceClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS(klass); FuDeviceClass *klass_device = FU_DEVICE_CLASS(klass); + object_class->finalize = fu_tpm_device_finalize; - klass_device->setup = fu_tpm_device_setup; - klass_device->probe = fu_tpm_device_probe; + klass_device->to_string = fu_tpm_device_to_string; } diff --git a/plugins/tpm/fu-tpm-device.h b/plugins/tpm/fu-tpm-device.h index 7649ce923..32852d881 100644 --- a/plugins/tpm/fu-tpm-device.h +++ b/plugins/tpm/fu-tpm-device.h @@ -9,7 +9,18 @@ #include #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 * 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); diff --git a/plugins/tpm/fu-tpm-v1-device.c b/plugins/tpm/fu-tpm-v1-device.c new file mode 100644 index 000000000..6e9f87f82 --- /dev/null +++ b/plugins/tpm/fu-tpm-v1-device.c @@ -0,0 +1,107 @@ +/* + * Copyright (C) 2018 Richard Hughes + * + * 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)); +} diff --git a/plugins/tpm/fu-tpm-v1-device.h b/plugins/tpm/fu-tpm-v1-device.h new file mode 100644 index 000000000..3b264f0b1 --- /dev/null +++ b/plugins/tpm/fu-tpm-v1-device.h @@ -0,0 +1,17 @@ +/* + * Copyright (C) 2018 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#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); diff --git a/plugins/tpm/fu-tpm-v2-device.c b/plugins/tpm/fu-tpm-v2-device.c new file mode 100644 index 000000000..bf6deee9f --- /dev/null +++ b/plugins/tpm/fu-tpm-v2-device.c @@ -0,0 +1,357 @@ +/* + * Copyright (C) 2018 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#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); +} diff --git a/plugins/tpm/fu-tpm-v2-device.h b/plugins/tpm/fu-tpm-v2-device.h new file mode 100644 index 000000000..2c3fb865c --- /dev/null +++ b/plugins/tpm/fu-tpm-v2-device.h @@ -0,0 +1,17 @@ +/* + * Copyright (C) 2018 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#pragma once + +#include + +#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); diff --git a/plugins/tpm/meson.build b/plugins/tpm/meson.build index e3c752daf..aeed78b73 100644 --- a/plugins/tpm/meson.build +++ b/plugins/tpm/meson.build @@ -15,6 +15,8 @@ shared_module('fu_plugin_tpm', sources : [ 'fu-plugin-tpm.c', 'fu-tpm-device.c', + 'fu-tpm-v1-device.c', + 'fu-tpm-v2-device.c', ], include_directories : [ root_incdir, @@ -33,4 +35,36 @@ shared_module('fu_plugin_tpm', 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 diff --git a/plugins/uefi-capsule/tests/tpm0/active b/plugins/tpm/tests/tpm0/active similarity index 100% rename from plugins/uefi-capsule/tests/tpm0/active rename to plugins/tpm/tests/tpm0/active diff --git a/plugins/uefi-capsule/tests/tpm0/caps b/plugins/tpm/tests/tpm0/caps similarity index 100% rename from plugins/uefi-capsule/tests/tpm0/caps rename to plugins/tpm/tests/tpm0/caps diff --git a/plugins/uefi-capsule/tests/tpm0/enabled b/plugins/tpm/tests/tpm0/enabled similarity index 100% rename from plugins/uefi-capsule/tests/tpm0/enabled rename to plugins/tpm/tests/tpm0/enabled diff --git a/plugins/uefi-capsule/tests/tpm0/owned b/plugins/tpm/tests/tpm0/owned similarity index 100% rename from plugins/uefi-capsule/tests/tpm0/owned rename to plugins/tpm/tests/tpm0/owned diff --git a/plugins/uefi-capsule/tests/tpm0/pcrs b/plugins/tpm/tests/tpm0/pcrs similarity index 100% rename from plugins/uefi-capsule/tests/tpm0/pcrs rename to plugins/tpm/tests/tpm0/pcrs diff --git a/plugins/uefi-capsule/fu-self-test.c b/plugins/uefi-capsule/fu-self-test.c index 3bfc63b61..b05a399ba 100644 --- a/plugins/uefi-capsule/fu-self-test.c +++ b/plugins/uefi-capsule/fu-self-test.c @@ -14,73 +14,6 @@ #include "fu-uefi-bgrt.h" #include "fu-uefi-cod-device.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 fu_uefi_ucs2_func(void) @@ -375,8 +308,6 @@ main(int argc, char **argv) g_setenv("G_MESSAGES_DEBUG", "all", TRUE); /* 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/bgrt", fu_uefi_bgrt_func); g_test_add_func("/uefi/framebuffer", fu_uefi_framebuffer_func); diff --git a/plugins/uefi-capsule/fu-uefi-device.c b/plugins/uefi-capsule/fu-uefi-device.c index 67cddd6a3..493227e3a 100644 --- a/plugins/uefi-capsule/fu-uefi-device.c +++ b/plugins/uefi-capsule/fu-uefi-device.c @@ -16,7 +16,6 @@ #include "fu-uefi-common.h" #include "fu-uefi-device.h" #include "fu-uefi-devpath.h" -#include "fu-uefi-pcrs.h" typedef struct { FuVolume *esp; @@ -592,39 +591,6 @@ fu_uefi_device_cleanup(FuDevice *device, FwupdInstallFlags flags, GError **error 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 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"); } - /* 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 */ if (priv->kind == FU_UEFI_DEVICE_KIND_FMP || priv->kind == FU_UEFI_DEVICE_KIND_DELL_TPM_FIRMWARE) diff --git a/plugins/uefi-capsule/fu-uefi-pcrs.c b/plugins/uefi-capsule/fu-uefi-pcrs.c deleted file mode 100644 index ead912f74..000000000 --- a/plugins/uefi-capsule/fu-uefi-pcrs.c +++ /dev/null @@ -1,296 +0,0 @@ -/* - * Copyright (C) 2018 Richard Hughes - * - * SPDX-License-Identifier: LGPL-2.1+ - */ - -#include "config.h" - -#include -#ifdef HAVE_TSS2 -#include -#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); -} diff --git a/plugins/uefi-capsule/fu-uefi-pcrs.h b/plugins/uefi-capsule/fu-uefi-pcrs.h deleted file mode 100644 index 4399133c8..000000000 --- a/plugins/uefi-capsule/fu-uefi-pcrs.h +++ /dev/null @@ -1,17 +0,0 @@ -/* - * Copyright (C) 2017 Richard Hughes - * - * 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); diff --git a/plugins/uefi-capsule/meson.build b/plugins/uefi-capsule/meson.build index 1fae91552..397003f41 100644 --- a/plugins/uefi-capsule/meson.build +++ b/plugins/uefi-capsule/meson.build @@ -42,7 +42,6 @@ shared_module('fu_plugin_uefi_capsule', 'fu-uefi-grub-device.c', 'fu-uefi-device.c', 'fu-uefi-devpath.c', - 'fu-uefi-pcrs.c', 'fu-uefi-update-info.c', backend_srcs, ], @@ -62,7 +61,6 @@ shared_module('fu_plugin_uefi_capsule', plugin_deps, platform_deps, efiboot, - tpm2tss, ], ) @@ -81,7 +79,6 @@ fwupdate = executable( 'fu-uefi-nvram-device.c', 'fu-uefi-grub-device.c', 'fu-uefi-devpath.c', - 'fu-uefi-pcrs.c', 'fu-uefi-update-info.c', backend_srcs, ], @@ -94,7 +91,6 @@ fwupdate = executable( plugin_deps, platform_deps, efiboot, - tpm2tss, ], link_with : [ fwupd, @@ -167,7 +163,6 @@ if get_option('tests') 'fu-uefi-nvram-device.c', 'fu-uefi-grub-device.c', 'fu-uefi-devpath.c', - 'fu-uefi-pcrs.c', 'fu-uefi-update-info.c', 'fu-ucs2.c', backend_srcs, @@ -181,7 +176,6 @@ if get_option('tests') plugin_deps, platform_deps, efiboot, - tpm2tss, ], link_with : [ fwupd,