1
0
Fork 0
qemu/hw/uefi/var-service-auth.c
Daniel Baumann ea34ddeea6
Adding upstream version 1:10.0.2+ds.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
2025-06-22 14:27:05 +02:00

361 lines
11 KiB
C

/*
* SPDX-License-Identifier: GPL-2.0-or-later
*
* uefi vars device - AuthVariableLib
*/
#include "qemu/osdep.h"
#include "qemu/error-report.h"
#include "system/dma.h"
#include "hw/uefi/var-service.h"
static const uint16_t name_pk[] = u"PK";
static const uint16_t name_kek[] = u"KEK";
static const uint16_t name_db[] = u"db";
static const uint16_t name_dbx[] = u"dbx";
static const uint16_t name_setup_mode[] = u"SetupMode";
static const uint16_t name_sigs_support[] = u"SignatureSupport";
static const uint16_t name_sb[] = u"SecureBoot";
static const uint16_t name_sb_enable[] = u"SecureBootEnable";
static const uint16_t name_custom_mode[] = u"CustomMode";
static const uint16_t name_vk[] = u"VendorKeys";
static const uint16_t name_vk_nv[] = u"VendorKeysNv";
static const uint32_t sigdb_attrs =
EFI_VARIABLE_NON_VOLATILE |
EFI_VARIABLE_BOOTSERVICE_ACCESS |
EFI_VARIABLE_RUNTIME_ACCESS |
EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS;
static void set_secure_boot(uefi_vars_state *uv, uint8_t sb)
{
uefi_vars_set_variable(uv, EfiGlobalVariable,
name_sb, sizeof(name_sb),
EFI_VARIABLE_BOOTSERVICE_ACCESS |
EFI_VARIABLE_RUNTIME_ACCESS,
&sb, sizeof(sb));
}
static void set_secure_boot_enable(uefi_vars_state *uv, uint8_t sbe)
{
uefi_vars_set_variable(uv, EfiSecureBootEnableDisable,
name_sb_enable, sizeof(name_sb_enable),
EFI_VARIABLE_NON_VOLATILE |
EFI_VARIABLE_BOOTSERVICE_ACCESS,
&sbe, sizeof(sbe));
}
static void set_setup_mode(uefi_vars_state *uv, uint8_t sm)
{
uefi_vars_set_variable(uv, EfiGlobalVariable,
name_setup_mode, sizeof(name_setup_mode),
EFI_VARIABLE_BOOTSERVICE_ACCESS |
EFI_VARIABLE_RUNTIME_ACCESS,
&sm, sizeof(sm));
}
static void set_custom_mode(uefi_vars_state *uv, uint8_t cm)
{
uefi_vars_set_variable(uv, EfiCustomModeEnable,
name_custom_mode, sizeof(name_custom_mode),
EFI_VARIABLE_NON_VOLATILE |
EFI_VARIABLE_BOOTSERVICE_ACCESS,
&cm, sizeof(cm));
}
static void set_signature_support(uefi_vars_state *uv)
{
QemuUUID sigs_support[5];
sigs_support[0] = EfiCertSha256Guid;
sigs_support[1] = EfiCertSha384Guid;
sigs_support[2] = EfiCertSha512Guid;
sigs_support[3] = EfiCertRsa2048Guid;
sigs_support[4] = EfiCertX509Guid;
uefi_vars_set_variable(uv, EfiGlobalVariable,
name_sigs_support, sizeof(name_sigs_support),
EFI_VARIABLE_BOOTSERVICE_ACCESS |
EFI_VARIABLE_RUNTIME_ACCESS,
sigs_support, sizeof(sigs_support));
}
static bool setup_mode_is_active(uefi_vars_state *uv)
{
uefi_variable *var;
uint8_t *value;
var = uefi_vars_find_variable(uv, EfiGlobalVariable,
name_setup_mode, sizeof(name_setup_mode));
if (var) {
value = var->data;
if (value[0] == SETUP_MODE) {
return true;
}
}
return false;
}
static bool custom_mode_is_active(uefi_vars_state *uv)
{
uefi_variable *var;
uint8_t *value;
var = uefi_vars_find_variable(uv, EfiCustomModeEnable,
name_custom_mode, sizeof(name_custom_mode));
if (var) {
value = var->data;
if (value[0] == CUSTOM_SECURE_BOOT_MODE) {
return true;
}
}
return false;
}
bool uefi_vars_is_sb_pk(uefi_variable *var)
{
if (qemu_uuid_is_equal(&var->guid, &EfiGlobalVariable) &&
uefi_str_equal(var->name, var->name_size, name_pk, sizeof(name_pk))) {
return true;
}
return false;
}
static bool uefi_vars_is_sb_kek(uefi_variable *var)
{
if (qemu_uuid_is_equal(&var->guid, &EfiGlobalVariable) &&
uefi_str_equal(var->name, var->name_size, name_kek, sizeof(name_kek))) {
return true;
}
return false;
}
static bool uefi_vars_is_sb_db(uefi_variable *var)
{
if (!qemu_uuid_is_equal(&var->guid, &EfiImageSecurityDatabase)) {
return false;
}
if (uefi_str_equal(var->name, var->name_size, name_db, sizeof(name_db))) {
return true;
}
if (uefi_str_equal(var->name, var->name_size, name_dbx, sizeof(name_dbx))) {
return true;
}
return false;
}
bool uefi_vars_is_sb_any(uefi_variable *var)
{
if (uefi_vars_is_sb_pk(var) ||
uefi_vars_is_sb_kek(var) ||
uefi_vars_is_sb_db(var)) {
return true;
}
return false;
}
static uefi_variable *uefi_vars_find_siglist(uefi_vars_state *uv,
uefi_variable *var)
{
if (uefi_vars_is_sb_pk(var)) {
return uefi_vars_find_variable(uv, EfiGlobalVariable,
name_pk, sizeof(name_pk));
}
if (uefi_vars_is_sb_kek(var)) {
return uefi_vars_find_variable(uv, EfiGlobalVariable,
name_pk, sizeof(name_pk));
}
if (uefi_vars_is_sb_db(var)) {
return uefi_vars_find_variable(uv, EfiGlobalVariable,
name_kek, sizeof(name_kek));
}
return NULL;
}
static efi_status uefi_vars_check_auth_2_sb(uefi_vars_state *uv,
uefi_variable *var,
mm_variable_access *va,
void *data,
uint64_t data_offset)
{
variable_auth_2 *auth = data;
uefi_variable *siglist;
if (custom_mode_is_active(uv)) {
/* no authentication in custom mode */
return EFI_SUCCESS;
}
if (setup_mode_is_active(uv) && !uefi_vars_is_sb_pk(var)) {
/* no authentication in setup mode (except PK) */
return EFI_SUCCESS;
}
if (auth->hdr_length == 24) {
/* no signature (auth->cert_data is empty) */
return EFI_SECURITY_VIOLATION;
}
siglist = uefi_vars_find_siglist(uv, var);
if (!siglist && setup_mode_is_active(uv) && uefi_vars_is_sb_pk(var)) {
/* check PK is self-signed */
uefi_variable tmp = {
.guid = EfiGlobalVariable,
.name = (uint16_t *)name_pk,
.name_size = sizeof(name_pk),
.attributes = sigdb_attrs,
.data = data + data_offset,
.data_size = va->data_size - data_offset,
};
return uefi_vars_check_pkcs7_2(&tmp, NULL, NULL, va, data);
}
return uefi_vars_check_pkcs7_2(siglist, NULL, NULL, va, data);
}
efi_status uefi_vars_check_auth_2(uefi_vars_state *uv, uefi_variable *var,
mm_variable_access *va, void *data)
{
variable_auth_2 *auth = data;
uint64_t data_offset;
efi_status status;
if (va->data_size < sizeof(*auth)) {
return EFI_SECURITY_VIOLATION;
}
if (uadd64_overflow(sizeof(efi_time), auth->hdr_length, &data_offset)) {
return EFI_SECURITY_VIOLATION;
}
if (va->data_size < data_offset) {
return EFI_SECURITY_VIOLATION;
}
if (auth->hdr_revision != 0x0200 ||
auth->hdr_cert_type != WIN_CERT_TYPE_EFI_GUID ||
!qemu_uuid_is_equal(&auth->guid_cert_type, &EfiCertTypePkcs7Guid)) {
return EFI_UNSUPPORTED;
}
if (uefi_vars_is_sb_any(var)) {
/* secure boot variables */
status = uefi_vars_check_auth_2_sb(uv, var, va, data, data_offset);
if (status != EFI_SUCCESS) {
return status;
}
} else {
/* other authenticated variables */
status = uefi_vars_check_pkcs7_2(NULL,
&var->digest, &var->digest_size,
va, data);
if (status != EFI_SUCCESS) {
return status;
}
}
/* checks passed, set variable data */
var->time = auth->timestamp;
if (va->data_size - data_offset > 0) {
var->data = g_malloc(va->data_size - data_offset);
memcpy(var->data, data + data_offset, va->data_size - data_offset);
var->data_size = va->data_size - data_offset;
}
return EFI_SUCCESS;
}
efi_status uefi_vars_check_secure_boot(uefi_vars_state *uv, uefi_variable *var)
{
uint8_t *value = var->data;
if (uefi_vars_is_sb_any(var)) {
if (var->attributes != sigdb_attrs) {
return EFI_INVALID_PARAMETER;
}
}
/* reject SecureBootEnable updates if force_secure_boot is set */
if (qemu_uuid_is_equal(&var->guid, &EfiSecureBootEnableDisable) &&
uefi_str_equal(var->name, var->name_size,
name_sb_enable, sizeof(name_sb_enable)) &&
uv->force_secure_boot &&
value[0] != SECURE_BOOT_ENABLE) {
return EFI_WRITE_PROTECTED;
}
/* reject CustomMode updates if disable_custom_mode is set */
if (qemu_uuid_is_equal(&var->guid, &EfiCustomModeEnable) &&
uefi_str_equal(var->name, var->name_size,
name_custom_mode, sizeof(name_custom_mode)) &&
uv->disable_custom_mode) {
return EFI_WRITE_PROTECTED;
}
return EFI_SUCCESS;
}
/* AuthVariableLibInitialize */
void uefi_vars_auth_init(uefi_vars_state *uv)
{
uefi_variable *pk_var, *sbe_var;
uint8_t platform_mode, sb, sbe, vk;
/* SetupMode */
pk_var = uefi_vars_find_variable(uv, EfiGlobalVariable,
name_pk, sizeof(name_pk));
if (!pk_var) {
platform_mode = SETUP_MODE;
} else {
platform_mode = USER_MODE;
}
set_setup_mode(uv, platform_mode);
/* SignatureSupport */
set_signature_support(uv);
/* SecureBootEnable */
sbe = SECURE_BOOT_DISABLE;
sbe_var = uefi_vars_find_variable(uv, EfiSecureBootEnableDisable,
name_sb_enable, sizeof(name_sb_enable));
if (sbe_var) {
if (platform_mode == USER_MODE) {
sbe = ((uint8_t *)sbe_var->data)[0];
}
} else if (platform_mode == USER_MODE) {
sbe = SECURE_BOOT_ENABLE;
set_secure_boot_enable(uv, sbe);
}
if (uv->force_secure_boot && sbe != SECURE_BOOT_ENABLE) {
sbe = SECURE_BOOT_ENABLE;
set_secure_boot_enable(uv, sbe);
}
/* SecureBoot */
if ((sbe == SECURE_BOOT_ENABLE) && (platform_mode == USER_MODE)) {
sb = SECURE_BOOT_MODE_ENABLE;
} else {
sb = SECURE_BOOT_MODE_DISABLE;
}
set_secure_boot(uv, sb);
/* CustomMode */
set_custom_mode(uv, STANDARD_SECURE_BOOT_MODE);
vk = 0;
uefi_vars_set_variable(uv, EfiGlobalVariable,
name_vk_nv, sizeof(name_vk_nv),
EFI_VARIABLE_NON_VOLATILE |
EFI_VARIABLE_BOOTSERVICE_ACCESS |
EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS,
&vk, sizeof(vk));
uefi_vars_set_variable(uv, EfiGlobalVariable,
name_vk, sizeof(name_vk),
EFI_VARIABLE_BOOTSERVICE_ACCESS |
EFI_VARIABLE_RUNTIME_ACCESS,
&vk, sizeof(vk));
/* flush to disk */
uefi_vars_json_save(uv);
}