mirror of
https://git.proxmox.com/git/fwupd
synced 2025-08-03 18:55:54 +00:00
Add a new plugin to get more Intel ME MCA data
This allows us to get the OEM Public Key BootGuard hashes. Also add a new HSI test for leaked bootguard keys.
This commit is contained in:
parent
ef04d0578f
commit
0f8ec55f46
27
docs/hsi.md
27
docs/hsi.md
@ -350,6 +350,33 @@ To meet HSI-1 on systems that run this test, the result must be `locked`. *[v1.5
|
||||
|
||||
- [Chromium documentation for Intel ME](https://chromium.googlesource.com/chromiumos/third_party/flashrom/+/master/Documentation/mysteries_intel.txt)
|
||||
|
||||
<a id="org.fwupd.hsi.Mei.BootGuardPlatformKey"></a>
|
||||
|
||||
### [ME BootGuard Platform Key](#org.fwupd.hsi.Mei.BootGuardPlatformKey)
|
||||
|
||||
The BootGuard Platform Key is fused into the CPU PCH during manufacturing by the OEM.
|
||||
|
||||
At bootup, an authenticated code module computes a hash of the Platform Key and and compares it
|
||||
with the one stored in field-programmable fuses.
|
||||
If the key matches the ACM will pass control to the firmware, otherwise the boot process will stop.
|
||||
|
||||
In 2022 a number of Platform **secret** Keys were leaked by Lenovo and confirmed by Intel.
|
||||
|
||||
**Impact:** A custom system firmware can be signed using the leaked private key to completely
|
||||
disable UEFI Secure Boot and allow complete persistent compromise of the affected machine.
|
||||
|
||||
**Possible results:**
|
||||
|
||||
- `valid`: device uses a BootGuard Platform Key that is not known to be compromised
|
||||
- `not-valid`: device uses a key that is compromised
|
||||
|
||||
To meet HSI-1 on systems that run this test, the result must be `valid`. *[v1.8.7]*
|
||||
|
||||
**References:**
|
||||
|
||||
- [Intel leak checker](https://github.com/phretor/intel-leak-checker/)
|
||||
- [Tom's Hardware Article](https://www.tomshardware.com/news/intel-confirms-6gb-alder-lake-bios-source-code-leak-new-details-emerge)
|
||||
|
||||
<a id="org.fwupd.hsi.Mei.Version"></a>
|
||||
|
||||
### [CSME Version](#org.fwupd.hsi.Mei.Version)
|
||||
|
@ -166,6 +166,14 @@ G_BEGIN_DECLS
|
||||
* Since: 1.5.0
|
||||
**/
|
||||
#define FWUPD_SECURITY_ATTR_ID_MEI_OVERRIDE_STRAP "org.fwupd.hsi.Mei.OverrideStrap"
|
||||
/**
|
||||
* FWUPD_SECURITY_ATTR_ID_MEI_KEY_MANIFEST:
|
||||
*
|
||||
* Host Security ID attribute for Intel ME Key Manifest
|
||||
*
|
||||
* Since: 1.8.7
|
||||
**/
|
||||
#define FWUPD_SECURITY_ATTR_ID_MEI_KEY_MANIFEST "org.fwupd.hsi.Mei.KeyManifest"
|
||||
/**
|
||||
* FWUPD_SECURITY_ATTR_ID_MEI_VERSION:
|
||||
*
|
||||
|
@ -157,6 +157,8 @@ fu_mei_device_connect(FuMeiDevice *self, guchar req_protocol_version, GError **e
|
||||
|
||||
if (!fwupd_guid_from_string(priv->uuid, &guid_le, FWUPD_GUID_FLAG_MIXED_ENDIAN, error))
|
||||
return FALSE;
|
||||
if (g_getenv("FU_MEI_DEVICE_DEBUG") != NULL)
|
||||
fu_dump_raw(G_LOG_DOMAIN, "guid_le", (guint8 *)&guid_le, sizeof(guid_le));
|
||||
memcpy(&data.in_client_uuid, &guid_le, sizeof(guid_le));
|
||||
if (!fu_udev_device_ioctl(FU_UDEV_DEVICE(self),
|
||||
IOCTL_MEI_CONNECT_CLIENT,
|
||||
|
@ -330,6 +330,7 @@ static struct {
|
||||
{FWUPD_SECURITY_ATTR_ID_IOMMU, FWUPD_SECURITY_ATTR_LEVEL_IMPORTANT},
|
||||
{FWUPD_SECURITY_ATTR_ID_MEI_MANUFACTURING_MODE, FWUPD_SECURITY_ATTR_LEVEL_CRITICAL},
|
||||
{FWUPD_SECURITY_ATTR_ID_MEI_OVERRIDE_STRAP, FWUPD_SECURITY_ATTR_LEVEL_CRITICAL},
|
||||
{FWUPD_SECURITY_ATTR_ID_MEI_KEY_MANIFEST, FWUPD_SECURITY_ATTR_LEVEL_CRITICAL},
|
||||
{FWUPD_SECURITY_ATTR_ID_MEI_VERSION, FWUPD_SECURITY_ATTR_LEVEL_CRITICAL},
|
||||
{FWUPD_SECURITY_ATTR_ID_PLATFORM_DEBUG_ENABLED, FWUPD_SECURITY_ATTR_LEVEL_CRITICAL},
|
||||
{FWUPD_SECURITY_ATTR_ID_PLATFORM_DEBUG_LOCKED, FWUPD_SECURITY_ATTR_LEVEL_IMPORTANT},
|
||||
|
19
plugins/intel-me/README.md
Normal file
19
plugins/intel-me/README.md
Normal file
@ -0,0 +1,19 @@
|
||||
# Intel ME
|
||||
|
||||
## Introduction
|
||||
|
||||
This plugin is used to talk to the Intel ME device, typically CSME.
|
||||
|
||||
It allows us to get the Platform Key as used for BootGuard.
|
||||
|
||||
## GUID Generation
|
||||
|
||||
These devices use the existing GUIDs provided by the ME host interfaces.
|
||||
|
||||
## Vendor ID Security
|
||||
|
||||
The devices are not upgradable and thus require no vendor ID set.
|
||||
|
||||
## External Interface Access
|
||||
|
||||
This plugin requires `ioctl(IOCTL_MEI_CONNECT_CLIENT)` to `/dev/mei0`.
|
99
plugins/intel-me/fu-intel-me-common.c
Normal file
99
plugins/intel-me/fu-intel-me-common.c
Normal file
@ -0,0 +1,99 @@
|
||||
/*
|
||||
* Copyright (C) 2022 Richard Hughes <richard@hughsie.com>
|
||||
*
|
||||
* SPDX-License-Identifier: LGPL-2.1+
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "fu-intel-me-common.h"
|
||||
|
||||
gboolean
|
||||
fu_intel_me_mkhi_result_to_error(FuMkhiResult result, GError **error)
|
||||
{
|
||||
if (result == MKHI_STATUS_SUCCESS)
|
||||
return TRUE;
|
||||
|
||||
switch (result) {
|
||||
case MKHI_STATUS_NOT_SUPPORTED:
|
||||
case MKHI_STATUS_NOT_AVAILABLE:
|
||||
case MKHI_STATUS_NOT_SET:
|
||||
g_set_error(error,
|
||||
G_IO_ERROR,
|
||||
G_IO_ERROR_NOT_SUPPORTED,
|
||||
"not supported [0x%x]",
|
||||
result);
|
||||
break;
|
||||
default:
|
||||
g_set_error(error, G_IO_ERROR, G_IO_ERROR_FAILED, "generic failure [0x%x]", result);
|
||||
break;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
gboolean
|
||||
fu_intel_me_mkhi_verify_header(const FuMkhiHeader hdr_req,
|
||||
const FuMkhiHeader hdr_res,
|
||||
GError **error)
|
||||
{
|
||||
if (hdr_req.group_id != hdr_res.group_id) {
|
||||
g_set_error(error,
|
||||
G_IO_ERROR,
|
||||
G_IO_ERROR_INVALID_DATA,
|
||||
"invalid response group ID, requested 0x%x and got 0x%x",
|
||||
hdr_req.group_id,
|
||||
hdr_res.group_id);
|
||||
return FALSE;
|
||||
}
|
||||
if (hdr_req.command != hdr_res.command) {
|
||||
g_set_error(error,
|
||||
G_IO_ERROR,
|
||||
G_IO_ERROR_INVALID_DATA,
|
||||
"invalid response command, requested 0x%x and got 0x%x",
|
||||
(guint)hdr_req.command,
|
||||
(guint)hdr_res.command);
|
||||
return FALSE;
|
||||
}
|
||||
if (!hdr_res.is_resp) {
|
||||
g_set_error_literal(error,
|
||||
G_IO_ERROR,
|
||||
G_IO_ERROR_INVALID_DATA,
|
||||
"invalid response group ID, not a response!");
|
||||
return FALSE;
|
||||
}
|
||||
return fu_intel_me_mkhi_result_to_error(hdr_res.result, error);
|
||||
}
|
||||
|
||||
GString *
|
||||
fu_intel_me_convert_checksum(GByteArray *buf, GError **error)
|
||||
{
|
||||
gboolean seen_non00_data = FALSE;
|
||||
gboolean seen_nonff_data = FALSE;
|
||||
g_autoptr(GString) checksum = g_string_new(NULL);
|
||||
|
||||
/* create checksum, but only if non-zero and set */
|
||||
for (gsize i = 0; i < buf->len; i++) {
|
||||
if (!seen_non00_data && buf->data[i] != 0x00)
|
||||
seen_non00_data = TRUE;
|
||||
if (!seen_nonff_data && buf->data[i] != 0xFF)
|
||||
seen_nonff_data = TRUE;
|
||||
g_string_append_printf(checksum, "%02x", buf->data[i]);
|
||||
}
|
||||
if (!seen_non00_data) {
|
||||
g_set_error_literal(error,
|
||||
G_IO_ERROR,
|
||||
G_IO_ERROR_NOT_INITIALIZED,
|
||||
"buffer was all 0x00");
|
||||
return NULL;
|
||||
}
|
||||
if (!seen_nonff_data) {
|
||||
g_set_error_literal(error,
|
||||
G_IO_ERROR,
|
||||
G_IO_ERROR_NOT_INITIALIZED,
|
||||
"buffer was all 0xFF");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* success */
|
||||
return g_steal_pointer(&checksum);
|
||||
}
|
64
plugins/intel-me/fu-intel-me-common.h
Normal file
64
plugins/intel-me/fu-intel-me-common.h
Normal file
@ -0,0 +1,64 @@
|
||||
/*
|
||||
* Copyright (C) 2022 Richard Hughes <richard@hughsie.com>
|
||||
*
|
||||
* SPDX-License-Identifier: LGPL-2.1+
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <gio/gio.h>
|
||||
|
||||
typedef struct __attribute__((packed)) {
|
||||
guint8 group_id;
|
||||
guint8 command : 7;
|
||||
guint8 is_resp : 1;
|
||||
guint8 rsvd;
|
||||
guint8 result;
|
||||
} FuMkhiHeader;
|
||||
|
||||
typedef enum {
|
||||
MKHI_GROUP_ID_CBM,
|
||||
MKHI_GROUP_ID_PM, /* no longer used */
|
||||
MKHI_GROUP_ID_PWD,
|
||||
MKHI_GROUP_ID_FWCAPS,
|
||||
MKHI_GROUP_ID_APP, /* no longer used */
|
||||
MKHI_GROUP_ID_FWUPDATE, /* for manufacturing downgrade */
|
||||
MKHI_GROUP_ID_FIRMWARE_UPDATE,
|
||||
MKHI_GROUP_ID_BIST,
|
||||
MKHI_GROUP_ID_MDES,
|
||||
MKHI_GROUP_ID_ME_DBG,
|
||||
MKHI_GROUP_ID_MCA, /* sometimes called "FPF" */
|
||||
MKHI_GROUP_ID_GEN = 0xFF
|
||||
} FuMkhiGroupId;
|
||||
|
||||
#define MCA_READ_FILE_EX 0x02
|
||||
#define MCA_READ_FILE_EX_CMD 0x0A
|
||||
|
||||
typedef enum {
|
||||
MKHI_STATUS_SUCCESS,
|
||||
MKHI_STATUS_INVALID_STATE,
|
||||
MKHI_STATUS_MESSAGE_SKIPPED,
|
||||
MKHI_STATUS_SIZE_ERROR = 0x05,
|
||||
MKHI_STATUS_NOT_SET = 0x0F, /* guessed */
|
||||
MKHI_STATUS_NOT_AVAILABLE = 0x18, /* guessed */
|
||||
MKHI_STATUS_INVALID_ACCESS = 0x84,
|
||||
MKHI_STATUS_INVALID_PARAMS = 0x85,
|
||||
MKHI_STATUS_NOT_READY = 0x88,
|
||||
MKHI_STATUS_NOT_SUPPORTED = 0x89,
|
||||
MKHI_STATUS_INVALID_ADDRESS = 0x8C,
|
||||
MKHI_STATUS_INVALID_COMMAND = 0x8D,
|
||||
MKHI_STATUS_FAILURE = 0x9E,
|
||||
MKHI_STATUS_INVALID_RESOURCE = 0xE4,
|
||||
MKHI_STATUS_RESOURCE_IN_USE = 0xE5,
|
||||
MKHI_STATUS_NO_RESOURCE = 0xE6,
|
||||
MKHI_STATUS_GENERAL_ERROR = 0xFF
|
||||
} FuMkhiResult;
|
||||
|
||||
GString *
|
||||
fu_intel_me_convert_checksum(GByteArray *buf, GError **error);
|
||||
gboolean
|
||||
fu_intel_me_mkhi_result_to_error(FuMkhiResult result, GError **error);
|
||||
gboolean
|
||||
fu_intel_me_mkhi_verify_header(const FuMkhiHeader hdr_req,
|
||||
const FuMkhiHeader hdr_res,
|
||||
GError **error);
|
191
plugins/intel-me/fu-intel-me-heci-device.c
Normal file
191
plugins/intel-me/fu-intel-me-heci-device.c
Normal file
@ -0,0 +1,191 @@
|
||||
/*
|
||||
* Copyright (C) 2022 Richard Hughes <richard@hughsie.com>
|
||||
*
|
||||
* SPDX-License-Identifier: LGPL-2.1+
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <fwupdplugin.h>
|
||||
|
||||
#include "fu-intel-me-common.h"
|
||||
#include "fu-intel-me-heci-device.h"
|
||||
|
||||
G_DEFINE_TYPE(FuIntelMeHeciDevice, fu_intel_me_heci_device, FU_TYPE_MEI_DEVICE)
|
||||
|
||||
#define FU_INTEL_ME_HECI_DEVICE_TIMEOUT 200 /* ms */
|
||||
|
||||
static gboolean
|
||||
fu_intel_me_heci_device_open(FuDevice *device, GError **error)
|
||||
{
|
||||
/* open then create context */
|
||||
if (!FU_DEVICE_CLASS(fu_intel_me_heci_device_parent_class)->open(device, error))
|
||||
return FALSE;
|
||||
return fu_mei_device_connect(FU_MEI_DEVICE(device), 0, error);
|
||||
}
|
||||
|
||||
static GByteArray *
|
||||
fu_intel_me_heci_device_read_file_response(GByteArray *buf_res, guint32 datasz_req, GError **error)
|
||||
{
|
||||
guint32 datasz_res = 0;
|
||||
g_autoptr(GByteArray) buf = g_byte_array_new();
|
||||
|
||||
/* verify payload size */
|
||||
if (!fu_memread_uint32_safe(buf_res->data,
|
||||
buf_res->len,
|
||||
sizeof(FuMkhiHeader),
|
||||
&datasz_res,
|
||||
G_LITTLE_ENDIAN,
|
||||
error))
|
||||
return NULL;
|
||||
if (datasz_res > datasz_req || datasz_res == 0x0) {
|
||||
g_set_error(error,
|
||||
G_IO_ERROR,
|
||||
G_IO_ERROR_INVALID_DATA,
|
||||
"invalid response data size, requested 0x%x and got 0x%x",
|
||||
datasz_req,
|
||||
datasz_res);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* copy out payload */
|
||||
for (gsize i = 0; i < datasz_res; i++) {
|
||||
guint8 tmp = 0;
|
||||
if (!fu_memread_uint8_safe(buf_res->data,
|
||||
buf_res->len,
|
||||
sizeof(FuMkhiHeader) + sizeof(guint32) + i,
|
||||
&tmp,
|
||||
error))
|
||||
return NULL;
|
||||
fu_byte_array_append_uint8(buf, tmp);
|
||||
}
|
||||
|
||||
/* success */
|
||||
return g_steal_pointer(&buf);
|
||||
}
|
||||
|
||||
GByteArray *
|
||||
fu_intel_me_heci_device_read_file(FuIntelMeHeciDevice *self, const gchar *filename, GError **error)
|
||||
{
|
||||
FuMkhiHeader hdr_res = {0};
|
||||
gsize filenamesz = strlen(filename);
|
||||
guint datasz_req = 0x80;
|
||||
g_autoptr(GByteArray) buf_fn = g_byte_array_new();
|
||||
g_autoptr(GByteArray) buf_req = g_byte_array_new();
|
||||
g_autoptr(GByteArray) buf_res = g_byte_array_new();
|
||||
const FuMkhiHeader hdr_req = {.group_id = MKHI_GROUP_ID_MCA, .command = MCA_READ_FILE_EX};
|
||||
|
||||
/* filename must be a specific size */
|
||||
fu_byte_array_set_size(buf_fn, 0x40, 0x0);
|
||||
if (!fu_memcpy_safe(buf_fn->data,
|
||||
buf_fn->len - 1,
|
||||
0x0,
|
||||
(const guint8 *)filename,
|
||||
filenamesz,
|
||||
0x0,
|
||||
filenamesz,
|
||||
error))
|
||||
return NULL;
|
||||
|
||||
/* request */
|
||||
g_byte_array_append(buf_req, (const guint8 *)&hdr_req, sizeof(hdr_req));
|
||||
g_byte_array_append(buf_req, buf_fn->data, buf_fn->len); /* Filename */
|
||||
fu_byte_array_append_uint32(buf_req, 0x0, G_LITTLE_ENDIAN); /* Offset */
|
||||
fu_byte_array_append_uint32(buf_req, datasz_req, G_LITTLE_ENDIAN); /* DataSize */
|
||||
fu_byte_array_append_uint8(buf_req, (1 << 3)); /* Flags?? */
|
||||
if (!fu_mei_device_write(FU_MEI_DEVICE(self),
|
||||
buf_req->data,
|
||||
buf_req->len,
|
||||
FU_INTEL_ME_HECI_DEVICE_TIMEOUT,
|
||||
error))
|
||||
return NULL;
|
||||
|
||||
/* response */
|
||||
fu_byte_array_set_size(buf_res, sizeof(hdr_res) + sizeof(guint32) + datasz_req, 0x0);
|
||||
if (!fu_mei_device_read(FU_MEI_DEVICE(self),
|
||||
buf_res->data,
|
||||
buf_res->len,
|
||||
NULL,
|
||||
FU_INTEL_ME_HECI_DEVICE_TIMEOUT,
|
||||
error))
|
||||
return NULL;
|
||||
|
||||
/* verify header */
|
||||
if (!fu_memcpy_safe((guint8 *)&hdr_res,
|
||||
sizeof(hdr_res),
|
||||
0x0, /* dst */
|
||||
buf_res->data,
|
||||
buf_res->len,
|
||||
0x0, /* src */
|
||||
sizeof(hdr_req),
|
||||
error))
|
||||
return NULL;
|
||||
if (!fu_intel_me_mkhi_verify_header(hdr_req, hdr_res, error))
|
||||
return NULL;
|
||||
return fu_intel_me_heci_device_read_file_response(buf_res, datasz_req, error);
|
||||
}
|
||||
|
||||
GByteArray *
|
||||
fu_intel_me_heci_device_read_file_ex(FuIntelMeHeciDevice *self,
|
||||
guint32 file_id,
|
||||
guint32 section,
|
||||
guint32 datasz_req,
|
||||
GError **error)
|
||||
{
|
||||
FuMkhiHeader hdr_res = {0};
|
||||
g_autoptr(GByteArray) buf_req = g_byte_array_new();
|
||||
g_autoptr(GByteArray) buf_res = g_byte_array_new();
|
||||
const FuMkhiHeader hdr_req = {.group_id = MKHI_GROUP_ID_MCA,
|
||||
.command = MCA_READ_FILE_EX_CMD};
|
||||
|
||||
/* request */
|
||||
g_byte_array_append(buf_req, (const guint8 *)&hdr_req, sizeof(hdr_req));
|
||||
fu_byte_array_append_uint32(buf_req, file_id, G_LITTLE_ENDIAN); /* FileId */
|
||||
fu_byte_array_append_uint32(buf_req, 0x0, G_LITTLE_ENDIAN); /* Offset */
|
||||
fu_byte_array_append_uint32(buf_req, datasz_req, G_LITTLE_ENDIAN); /* DataSize */
|
||||
fu_byte_array_append_uint8(buf_req, section); /* Flags */
|
||||
if (!fu_mei_device_write(FU_MEI_DEVICE(self),
|
||||
buf_req->data,
|
||||
buf_req->len,
|
||||
FU_INTEL_ME_HECI_DEVICE_TIMEOUT,
|
||||
error))
|
||||
return NULL;
|
||||
|
||||
/* response */
|
||||
fu_byte_array_set_size(buf_res, sizeof(hdr_res) + sizeof(guint32) + datasz_req, 0x0);
|
||||
if (!fu_mei_device_read(FU_MEI_DEVICE(self),
|
||||
buf_res->data,
|
||||
buf_res->len,
|
||||
NULL,
|
||||
FU_INTEL_ME_HECI_DEVICE_TIMEOUT,
|
||||
error))
|
||||
return NULL;
|
||||
|
||||
/* verify header */
|
||||
if (!fu_memcpy_safe((guint8 *)&hdr_res,
|
||||
sizeof(hdr_res),
|
||||
0x0, /* dst */
|
||||
buf_res->data,
|
||||
buf_res->len,
|
||||
0x0, /* src */
|
||||
sizeof(hdr_req),
|
||||
error))
|
||||
return NULL;
|
||||
if (!fu_intel_me_mkhi_verify_header(hdr_req, hdr_res, error))
|
||||
return NULL;
|
||||
return fu_intel_me_heci_device_read_file_response(buf_res, datasz_req, error);
|
||||
}
|
||||
|
||||
static void
|
||||
fu_intel_me_heci_device_init(FuIntelMeHeciDevice *self)
|
||||
{
|
||||
fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_INTERNAL);
|
||||
fu_device_add_icon(FU_DEVICE(self), "computer");
|
||||
}
|
||||
|
||||
static void
|
||||
fu_intel_me_heci_device_class_init(FuIntelMeHeciDeviceClass *klass)
|
||||
{
|
||||
FuDeviceClass *klass_device = FU_DEVICE_CLASS(klass);
|
||||
klass_device->open = fu_intel_me_heci_device_open;
|
||||
}
|
29
plugins/intel-me/fu-intel-me-heci-device.h
Normal file
29
plugins/intel-me/fu-intel-me-heci-device.h
Normal file
@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Copyright (C) 2022 Richard Hughes <richard@hughsie.com>
|
||||
*
|
||||
* SPDX-License-Identifier: LGPL-2.1+
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <fwupdplugin.h>
|
||||
|
||||
#define FU_TYPE_INTEL_ME_HECI_DEVICE (fu_intel_me_heci_device_get_type())
|
||||
G_DECLARE_DERIVABLE_TYPE(FuIntelMeHeciDevice,
|
||||
fu_intel_me_heci_device,
|
||||
FU,
|
||||
INTEL_ME_HECI_DEVICE,
|
||||
FuMeiDevice)
|
||||
|
||||
struct _FuIntelMeHeciDeviceClass {
|
||||
FuMeiDeviceClass parent_class;
|
||||
};
|
||||
|
||||
GByteArray *
|
||||
fu_intel_me_heci_device_read_file(FuIntelMeHeciDevice *self, const gchar *filename, GError **error);
|
||||
GByteArray *
|
||||
fu_intel_me_heci_device_read_file_ex(FuIntelMeHeciDevice *self,
|
||||
guint32 file_id,
|
||||
guint32 section,
|
||||
guint32 datasz_req,
|
||||
GError **error);
|
173
plugins/intel-me/fu-intel-me-mca-device.c
Normal file
173
plugins/intel-me/fu-intel-me-mca-device.c
Normal file
@ -0,0 +1,173 @@
|
||||
/*
|
||||
* Copyright (C) 2022 Richard Hughes <richard@hughsie.com>
|
||||
*
|
||||
* SPDX-License-Identifier: LGPL-2.1+
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "fu-intel-me-common.h"
|
||||
#include "fu-intel-me-mca-device.h"
|
||||
|
||||
struct _FuIntelMeMcaDevice {
|
||||
FuIntelMeHeciDevice parent_instance;
|
||||
gboolean using_leaked_km;
|
||||
};
|
||||
|
||||
G_DEFINE_TYPE(FuIntelMeMcaDevice, fu_intel_me_mca_device, FU_TYPE_INTEL_ME_HECI_DEVICE)
|
||||
|
||||
#define MCA_SECTION_ME 0x00 /* OEM Public Key Hash ME FW */
|
||||
#define MCA_SECTION_UEP 0x04 /* OEM Public Key Hash UEP */
|
||||
#define MCA_SECTION_FPF 0x08 /* OEM Public Key Hash FPF */
|
||||
|
||||
static const gchar *
|
||||
fu_intel_me_mca_device_section_to_string(guint8 section)
|
||||
{
|
||||
if (section == MCA_SECTION_ME)
|
||||
return "ME";
|
||||
if (section == MCA_SECTION_UEP)
|
||||
return "UEP";
|
||||
if (section == MCA_SECTION_FPF)
|
||||
return "FPF";
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
fu_intel_me_mca_device_add_checksum_for_id(FuIntelMeMcaDevice *self,
|
||||
guint32 file_id,
|
||||
guint32 section,
|
||||
GError **error)
|
||||
{
|
||||
g_autoptr(GByteArray) buf = NULL;
|
||||
g_autoptr(GString) checksum = NULL;
|
||||
|
||||
/*
|
||||
* Call READ_FILE_EX with a larger-than-required data size -- which hopefully works when
|
||||
* SHA512 results start being returned too.
|
||||
*
|
||||
* CometLake: 0x20 (SHA256)
|
||||
* TigerLake: 0x30 (SHA384)
|
||||
*/
|
||||
buf = fu_intel_me_heci_device_read_file_ex(FU_INTEL_ME_HECI_DEVICE(self),
|
||||
file_id,
|
||||
section,
|
||||
0x40,
|
||||
error);
|
||||
if (buf == NULL)
|
||||
return FALSE;
|
||||
|
||||
/* convert into checksum, but only if non-zero and set */
|
||||
checksum = fu_intel_me_convert_checksum(buf, error);
|
||||
if (checksum == NULL)
|
||||
return FALSE;
|
||||
fu_device_add_checksum(FU_DEVICE(self), checksum->str);
|
||||
|
||||
/* success */
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
fu_intel_me_mca_device_setup(FuDevice *device, GError **error)
|
||||
{
|
||||
FuIntelMeMcaDevice *self = FU_INTEL_ME_MCA_DEVICE(device);
|
||||
const guint32 sections[] = {MCA_SECTION_FPF, MCA_SECTION_UEP, MCA_SECTION_ME, G_MAXUINT32};
|
||||
const guint32 file_ids[] = {0x40002300, /* CometLake: OEM Public Key Hash */
|
||||
0x40005B00, /* TigerLake: 1st OEM Public Key Hash */
|
||||
0x40005C00 /* TigerLake: 2nd OEM Public Key Hash */,
|
||||
G_MAXUINT32};
|
||||
const gchar *leaked_kms[] = {"05a92e16da51d10882bfa7e3ba449184ce48e94fa9903e07983d2112ab"
|
||||
"54ecf20fbb07512cea2c13b167c0e252c6a704",
|
||||
"2e357bca116cf3da637bb5803be3550873eddb5a4431a49df1770aca83"
|
||||
"5d94853b458239d207653dce277910d9e5aa0b",
|
||||
"b52a825cf0be60027f12a226226b055ed68efaa9273695d45d859c0ed3"
|
||||
"3d063143974f4b4c59fabfc5afeadab0b00f09",
|
||||
NULL};
|
||||
|
||||
/* look for all the possible OEM Public Key hashes using the CML+ method */
|
||||
for (guint i = 0; file_ids[i] != G_MAXUINT32; i++) {
|
||||
for (guint j = 0; sections[j] != G_MAXUINT32; j++) {
|
||||
g_autoptr(GError) error_local = NULL;
|
||||
if (!fu_intel_me_mca_device_add_checksum_for_id(self,
|
||||
file_ids[i],
|
||||
sections[j],
|
||||
&error_local)) {
|
||||
if (g_error_matches(error_local,
|
||||
G_IO_ERROR,
|
||||
G_IO_ERROR_NOT_SUPPORTED) ||
|
||||
g_error_matches(error_local,
|
||||
G_IO_ERROR,
|
||||
G_IO_ERROR_NOT_INITIALIZED)) {
|
||||
continue;
|
||||
}
|
||||
g_warning("failed to get public key using file-id 0x%x, "
|
||||
"section %s [0x%x]: %s",
|
||||
file_ids[i],
|
||||
fu_intel_me_mca_device_section_to_string(sections[j]),
|
||||
sections[j],
|
||||
error_local->message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* no point even adding */
|
||||
if (fu_device_get_checksums(device)->len == 0) {
|
||||
g_set_error_literal(error,
|
||||
FWUPD_ERROR,
|
||||
FWUPD_ERROR_NOT_SUPPORTED,
|
||||
"no OEM public keys found");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* check for any of the leaked keys */
|
||||
for (guint i = 0; leaked_kms[i] != NULL; i++) {
|
||||
if (fu_device_has_checksum(self, leaked_kms[i])) {
|
||||
self->using_leaked_km = TRUE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* success */
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
fu_intel_me_mca_device_add_security_attrs(FuDevice *device, FuSecurityAttrs *attrs)
|
||||
{
|
||||
FuIntelMeMcaDevice *self = FU_INTEL_ME_MCA_DEVICE(device);
|
||||
g_autoptr(FwupdSecurityAttr) attr = NULL;
|
||||
|
||||
/* create attr */
|
||||
attr =
|
||||
fu_device_security_attr_new(FU_DEVICE(self), FWUPD_SECURITY_ATTR_ID_MEI_KEY_MANIFEST);
|
||||
fu_security_attrs_append(attrs, attr);
|
||||
|
||||
/* verify keys */
|
||||
if (fu_device_get_checksums(device)->len == 0) {
|
||||
fwupd_security_attr_add_flag(attr, FWUPD_SECURITY_ATTR_FLAG_MISSING_DATA);
|
||||
return;
|
||||
}
|
||||
if (self->using_leaked_km) {
|
||||
fwupd_security_attr_set_result(attr, FWUPD_SECURITY_ATTR_RESULT_NOT_VALID);
|
||||
return;
|
||||
}
|
||||
|
||||
/* success */
|
||||
fwupd_security_attr_add_flag(attr, FWUPD_SECURITY_ATTR_FLAG_SUCCESS);
|
||||
fwupd_security_attr_set_result(attr, FWUPD_SECURITY_ATTR_RESULT_VALID);
|
||||
}
|
||||
|
||||
static void
|
||||
fu_intel_me_mca_device_init(FuIntelMeMcaDevice *self)
|
||||
{
|
||||
fu_device_set_logical_id(FU_DEVICE(self), "MCA");
|
||||
fu_device_set_name(FU_DEVICE(self), "BootGuard Configuration");
|
||||
fu_device_add_parent_guid(FU_DEVICE(self), "main-system-firmware");
|
||||
}
|
||||
|
||||
static void
|
||||
fu_intel_me_mca_device_class_init(FuIntelMeMcaDeviceClass *klass)
|
||||
{
|
||||
FuDeviceClass *klass_device = FU_DEVICE_CLASS(klass);
|
||||
klass_device->setup = fu_intel_me_mca_device_setup;
|
||||
klass_device->add_security_attrs = fu_intel_me_mca_device_add_security_attrs;
|
||||
}
|
18
plugins/intel-me/fu-intel-me-mca-device.h
Normal file
18
plugins/intel-me/fu-intel-me-mca-device.h
Normal file
@ -0,0 +1,18 @@
|
||||
/*
|
||||
* Copyright (C) 2022 Richard Hughes <richard@hughsie.com>
|
||||
*
|
||||
* SPDX-License-Identifier: LGPL-2.1+
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <fwupdplugin.h>
|
||||
|
||||
#include "fu-intel-me-heci-device.h"
|
||||
|
||||
#define FU_TYPE_INTEL_ME_MCA_DEVICE (fu_intel_me_mca_device_get_type())
|
||||
G_DECLARE_FINAL_TYPE(FuIntelMeMcaDevice,
|
||||
fu_intel_me_mca_device,
|
||||
FU,
|
||||
INTEL_ME_MCA_DEVICE,
|
||||
FuIntelMeHeciDevice)
|
90
plugins/intel-me/fu-intel-me-mkhi-device.c
Normal file
90
plugins/intel-me/fu-intel-me-mkhi-device.c
Normal file
@ -0,0 +1,90 @@
|
||||
/*
|
||||
* Copyright (C) 2022 Richard Hughes <richard@hughsie.com>
|
||||
*
|
||||
* SPDX-License-Identifier: LGPL-2.1+
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "fu-intel-me-common.h"
|
||||
#include "fu-intel-me-mkhi-device.h"
|
||||
|
||||
struct _FuIntelMeMkhiDevice {
|
||||
FuIntelMeHeciDevice parent_instance;
|
||||
};
|
||||
|
||||
G_DEFINE_TYPE(FuIntelMeMkhiDevice, fu_intel_me_mkhi_device, FU_TYPE_INTEL_ME_HECI_DEVICE)
|
||||
|
||||
static gboolean
|
||||
fu_intel_me_mkhi_device_add_checksum_for_filename(FuIntelMeMkhiDevice *self,
|
||||
const gchar *filename,
|
||||
GError **error)
|
||||
{
|
||||
g_autoptr(GByteArray) buf = NULL;
|
||||
g_autoptr(GString) checksum = NULL;
|
||||
|
||||
/* read from the MFS */
|
||||
buf = fu_intel_me_heci_device_read_file(FU_INTEL_ME_HECI_DEVICE(self), filename, error);
|
||||
if (buf == NULL)
|
||||
return FALSE;
|
||||
|
||||
/* convert into checksum, but only if non-zero and set */
|
||||
checksum = fu_intel_me_convert_checksum(buf, error);
|
||||
if (checksum == NULL)
|
||||
return FALSE;
|
||||
fu_device_add_checksum(FU_DEVICE(self), checksum->str);
|
||||
|
||||
/* success */
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
fu_intel_me_mkhi_device_setup(FuDevice *device, GError **error)
|
||||
{
|
||||
FuIntelMeMkhiDevice *self = FU_INTEL_ME_MKHI_DEVICE(device);
|
||||
const gchar *fns[] = {"/fpf/OemCred", NULL};
|
||||
|
||||
/* this is the legacy way to get the hash, which is removed in newer ME versions due to
|
||||
* possible path traversal attacks */
|
||||
for (guint i = 0; fns[i] != NULL; i++) {
|
||||
g_autoptr(GError) error_local = NULL;
|
||||
g_autoptr(GByteArray) buf = NULL;
|
||||
if (!fu_intel_me_mkhi_device_add_checksum_for_filename(self,
|
||||
fns[i],
|
||||
&error_local)) {
|
||||
if (g_error_matches(error_local, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED)) {
|
||||
continue;
|
||||
}
|
||||
g_warning("failed to get public key using %s: %s",
|
||||
fns[i],
|
||||
error_local->message);
|
||||
}
|
||||
}
|
||||
|
||||
/* no point even adding */
|
||||
if (fu_device_get_checksums(device)->len == 0) {
|
||||
g_set_error_literal(error,
|
||||
FWUPD_ERROR,
|
||||
FWUPD_ERROR_NOT_SUPPORTED,
|
||||
"no OEM public keys found");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* success */
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
fu_intel_me_mkhi_device_init(FuIntelMeMkhiDevice *self)
|
||||
{
|
||||
fu_device_set_logical_id(FU_DEVICE(self), "MKHI");
|
||||
fu_device_set_name(FU_DEVICE(self), "BootGuard Configuration");
|
||||
fu_device_add_parent_guid(FU_DEVICE(self), "main-system-firmware");
|
||||
}
|
||||
|
||||
static void
|
||||
fu_intel_me_mkhi_device_class_init(FuIntelMeMkhiDeviceClass *klass)
|
||||
{
|
||||
FuDeviceClass *klass_device = FU_DEVICE_CLASS(klass);
|
||||
klass_device->setup = fu_intel_me_mkhi_device_setup;
|
||||
}
|
18
plugins/intel-me/fu-intel-me-mkhi-device.h
Normal file
18
plugins/intel-me/fu-intel-me-mkhi-device.h
Normal file
@ -0,0 +1,18 @@
|
||||
/*
|
||||
* Copyright (C) 2022 Richard Hughes <richard@hughsie.com>
|
||||
*
|
||||
* SPDX-License-Identifier: LGPL-2.1+
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <fwupdplugin.h>
|
||||
|
||||
#include "fu-intel-me-heci-device.h"
|
||||
|
||||
#define FU_TYPE_INTEL_ME_MKHI_DEVICE (fu_intel_me_mkhi_device_get_type())
|
||||
G_DECLARE_FINAL_TYPE(FuIntelMeMkhiDevice,
|
||||
fu_intel_me_mkhi_device,
|
||||
FU,
|
||||
INTEL_ME_MKHI_DEVICE,
|
||||
FuIntelMeHeciDevice)
|
38
plugins/intel-me/fu-intel-me-plugin.c
Normal file
38
plugins/intel-me/fu-intel-me-plugin.c
Normal file
@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright (C) 2022 Richard Hughes <richard@hughsie.com>
|
||||
*
|
||||
* SPDX-License-Identifier: LGPL-2.1+
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "fu-intel-me-mca-device.h"
|
||||
#include "fu-intel-me-mkhi-device.h"
|
||||
#include "fu-intel-me-plugin.h"
|
||||
|
||||
struct _FuIntelMePlugin {
|
||||
FuPlugin parent_instance;
|
||||
};
|
||||
|
||||
G_DEFINE_TYPE(FuIntelMePlugin, fu_intel_me_plugin, FU_TYPE_PLUGIN)
|
||||
|
||||
static void
|
||||
fu_intel_me_plugin_init(FuIntelMePlugin *self)
|
||||
{
|
||||
}
|
||||
|
||||
static void
|
||||
fu_intel_me_plugin_constructed(GObject *obj)
|
||||
{
|
||||
FuPlugin *plugin = FU_PLUGIN(obj);
|
||||
fu_plugin_add_udev_subsystem(plugin, "mei");
|
||||
fu_plugin_add_device_gtype(plugin, FU_TYPE_INTEL_ME_MCA_DEVICE);
|
||||
fu_plugin_add_device_gtype(plugin, FU_TYPE_INTEL_ME_MKHI_DEVICE);
|
||||
}
|
||||
|
||||
static void
|
||||
fu_intel_me_plugin_class_init(FuIntelMePluginClass *klass)
|
||||
{
|
||||
GObjectClass *object_class = G_OBJECT_CLASS(klass);
|
||||
object_class->constructed = fu_intel_me_plugin_constructed;
|
||||
}
|
11
plugins/intel-me/fu-intel-me-plugin.h
Normal file
11
plugins/intel-me/fu-intel-me-plugin.h
Normal file
@ -0,0 +1,11 @@
|
||||
/*
|
||||
* Copyright (C) 2022 Richard Hughes <richard@hughsie.com>
|
||||
*
|
||||
* SPDX-License-Identifier: LGPL-2.1+
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <fwupdplugin.h>
|
||||
|
||||
G_DECLARE_FINAL_TYPE(FuIntelMePlugin, fu_intel_me_plugin, FU, INTEL_ME_PLUGIN, FuPlugin)
|
9
plugins/intel-me/intel-me.quirk
Normal file
9
plugins/intel-me/intel-me.quirk
Normal file
@ -0,0 +1,9 @@
|
||||
# MKHI (legacy)
|
||||
[8e6a6715-9abc-4043-88ef-9e39c6f63e0f]
|
||||
Plugin = intel_me
|
||||
GType = FuIntelMeMkhiDevice
|
||||
|
||||
# MCA?
|
||||
[dd17041c-09ea-4b17-a271-5b989867ec65]
|
||||
Plugin = intel_me
|
||||
GType = FuIntelMeMcaDevice
|
18
plugins/intel-me/meson.build
Normal file
18
plugins/intel-me/meson.build
Normal file
@ -0,0 +1,18 @@
|
||||
if get_option('plugin_amt').disable_auto_if(host_machine.system() != 'linux').allowed()
|
||||
cargs = ['-DG_LOG_DOMAIN="FuPluginIntelMe"']
|
||||
|
||||
plugin_quirks += files('intel-me.quirk')
|
||||
plugin_builtins += static_library('fu_plugin_intel_me',
|
||||
sources: [
|
||||
'fu-intel-me-common.c',
|
||||
'fu-intel-me-plugin.c',
|
||||
'fu-intel-me-heci-device.c',
|
||||
'fu-intel-me-mca-device.c',
|
||||
'fu-intel-me-mkhi-device.c',
|
||||
],
|
||||
include_directories: plugin_incdirs,
|
||||
link_with: plugin_libs,
|
||||
c_args: cargs,
|
||||
dependencies: plugin_deps,
|
||||
)
|
||||
endif
|
@ -52,6 +52,7 @@ subdir('genesys')
|
||||
subdir('goodix-moc')
|
||||
subdir('gpio')
|
||||
subdir('hailuck')
|
||||
subdir('intel-me')
|
||||
subdir('intel-spi')
|
||||
subdir('intel-usb4')
|
||||
subdir('iommu')
|
||||
|
@ -146,6 +146,11 @@ fu_security_attr_get_name(FwupdSecurityAttr *attr)
|
||||
* end users on consumer boards */
|
||||
return g_strdup(_("MEI override"));
|
||||
}
|
||||
if (g_strcmp0(appstream_id, FWUPD_SECURITY_ATTR_ID_MEI_KEY_MANIFEST) == 0) {
|
||||
/* TRANSLATORS: Title: MEI = Intel Management Engine, and key refer
|
||||
* to the private/public key used to secure loading of firmware */
|
||||
return g_strdup(_("MEI key manifest"));
|
||||
}
|
||||
if (g_strcmp0(appstream_id, FWUPD_SECURITY_ATTR_ID_MEI_VERSION) == 0) {
|
||||
/* TRANSLATORS: Title: MEI = Intel Management Engine */
|
||||
const gchar *kind = fwupd_security_attr_get_metadata(attr, "kind");
|
||||
@ -322,6 +327,11 @@ fu_security_attr_get_title(FwupdSecurityAttr *attr)
|
||||
* boards */
|
||||
return _("Intel Management Engine Override");
|
||||
}
|
||||
if (g_strcmp0(appstream_id, FWUPD_SECURITY_ATTR_ID_MEI_KEY_MANIFEST) == 0) {
|
||||
/* TRANSLATORS: Title: MEI = Intel Management Engine, and key refers
|
||||
* to the private/public key used to secure loading of firmware */
|
||||
return g_strdup(_("MEI Key Manifest"));
|
||||
}
|
||||
if (g_strcmp0(appstream_id, FWUPD_SECURITY_ATTR_ID_MEI_VERSION) == 0) {
|
||||
/* TRANSLATORS: Title: MEI = Intel Management Engine */
|
||||
return _("Intel Management Engine Version");
|
||||
@ -496,6 +506,11 @@ fu_security_attr_get_description(FwupdSecurityAttr *attr)
|
||||
return _("Intel Management Engine Override disables checks for device software "
|
||||
"tampering.");
|
||||
}
|
||||
if (g_strcmp0(appstream_id, FWUPD_SECURITY_ATTR_ID_MEI_KEY_MANIFEST) == 0) {
|
||||
/* TRANSLATORS: longer description */
|
||||
return _("The Intel Management Engine Key Manifest must be valid so "
|
||||
"that the device firmware can be trusted by the CPU.");
|
||||
}
|
||||
if (g_strcmp0(appstream_id, FWUPD_SECURITY_ATTR_ID_MEI_VERSION) == 0) {
|
||||
/* TRANSLATORS: longer description */
|
||||
return _("The Intel Management Engine controls device components and needs "
|
||||
|
Loading…
Reference in New Issue
Block a user