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:
Richard Hughes 2022-10-11 13:40:49 +01:00
parent ef04d0578f
commit 0f8ec55f46
19 changed files with 831 additions and 0 deletions

View File

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

View File

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

View File

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

View File

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

View 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`.

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

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

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

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

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

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

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

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

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

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

View 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

View 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

View File

@ -52,6 +52,7 @@ subdir('genesys')
subdir('goodix-moc')
subdir('gpio')
subdir('hailuck')
subdir('intel-me')
subdir('intel-spi')
subdir('intel-usb4')
subdir('iommu')

View File

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