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