fwupd/plugins/intel-me/fu-intel-me-heci-device.c
Richard Hughes 0f8ec55f46 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.
2022-10-13 18:34:00 +01:00

192 lines
5.4 KiB
C

/*
* 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;
}