361 lines
11 KiB
C
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);
|
|
}
|