From 5eb00d39d3e57045b960f84054e476ab2bdc3603 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20=C5=BBygowski?= Date: Fri, 27 Jun 2025 15:06:13 +0200 Subject: [PATCH 01/28] DasharoPayloadPkg/PlatformBootManagerLib/: Skip printing hotkeys in SV Boot path MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Michał Żygowski From c91830e5638f81209e154870aa60603e135b15e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20=C5=BBygowski?= Date: Mon, 7 Jul 2025 17:13:48 +0200 Subject: [PATCH 02/28] SecurityPkg: Refactor DxeImageVerificationLib MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Make DxeImageVerificationLib more librar-ish. The same code with small modification is used in the SecureBootConfigDxe amd the DxeImageVerificationLib. Sovereign Boot Wizard will also need functions to verify images, which would introduce yet another duplication of code. The DxeImageVerificationLib had a lot of global variables holding internal state of the image being processed and the functions had to be executed in certain order to pass verification. Make the function less independent so they are callable with less external code. Signed-off-by: Michał Żygowski --- ArmVirtPkg/ArmVirtCloudHv.dsc | 3 +- ArmVirtPkg/ArmVirtQemu.dsc | 3 +- ArmVirtPkg/ArmVirtQemuKernel.dsc | 3 +- DasharoPayloadPkg/DasharoPayloadPkg.dsc | 4 +- EmulatorPkg/EmulatorPkg.dsc | 3 +- OvmfPkg/Bhyve/BhyveX64.dsc | 3 +- OvmfPkg/CloudHv/CloudHvX64.dsc | 4 +- OvmfPkg/IntelTdx/IntelTdxX64.dsc | 3 +- OvmfPkg/Microvm/MicrovmX64.dsc | 4 +- OvmfPkg/OvmfPkgIa32.dsc | 3 +- OvmfPkg/OvmfPkgIa32X64.dsc | 3 +- OvmfPkg/OvmfPkgX64.dsc | 3 +- OvmfPkg/RiscVVirt/RiscVVirtQemu.dsc | 3 +- .../Include/Library/DxeImageVerificationLib.h | 240 ++++ .../DxeImageVerificationHandler.c | 774 +++++++++++ .../DxeImageVerificationHandler.inf | 96 ++ .../DxeImageVerificationLib.c | 1215 +++++------------ .../DxeImageVerificationLib.inf | 12 +- ...onLib.h => ImageVerificationLibInternal.h} | 24 +- SecurityPkg/SecurityPkg.dec | 3 + SecurityPkg/SecurityPkg.dsc | 1 + .../SecureBootConfigDxe/SecureBootConfig.vfr | 14 +- .../SecureBootConfigDxe.inf | 1 + .../SecureBootConfigImpl.c | 849 +----------- .../SecureBootConfigImpl.h | 33 - .../SecureBootConfigStrings.uni | 2 + UefiPayloadPkg/UefiPayloadPkg.dsc | 3 +- 27 files changed, 1551 insertions(+), 1758 deletions(-) create mode 100644 SecurityPkg/Include/Library/DxeImageVerificationLib.h create mode 100644 SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationHandler.c create mode 100644 SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationHandler.inf rename SecurityPkg/Library/DxeImageVerificationLib/{DxeImageVerificationLib.h => ImageVerificationLibInternal.h} (87%) diff --git a/ArmVirtPkg/ArmVirtCloudHv.dsc b/ArmVirtPkg/ArmVirtCloudHv.dsc index ca1aa13e1f..a4d462082d 100644 --- a/ArmVirtPkg/ArmVirtCloudHv.dsc +++ b/ArmVirtPkg/ArmVirtCloudHv.dsc @@ -57,6 +57,7 @@ TpmMeasurementLib|MdeModulePkg/Library/TpmMeasurementLibNull/TpmMeasurementLibNull.inf TpmPlatformHierarchyLib|SecurityPkg/Library/PeiDxeTpmPlatformHierarchyLibNull/PeiDxeTpmPlatformHierarchyLib.inf + DxeImageVerificationLib|SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.inf !include MdePkg/MdeLibs.dsc.inc @@ -255,7 +256,7 @@ !if $(SECURE_BOOT_ENABLE) == TRUE MdeModulePkg/Universal/SecurityStubDxe/SecurityStubDxe.inf { - NULL|SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.inf + NULL|SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationHandler.inf } SecurityPkg/VariableAuthenticated/SecureBootConfigDxe/SecureBootConfigDxe.inf OvmfPkg/EnrollDefaultKeys/EnrollDefaultKeys.inf diff --git a/ArmVirtPkg/ArmVirtQemu.dsc b/ArmVirtPkg/ArmVirtQemu.dsc index 6193a4bb05..275660d561 100644 --- a/ArmVirtPkg/ArmVirtQemu.dsc +++ b/ArmVirtPkg/ArmVirtQemu.dsc @@ -95,6 +95,7 @@ !endif ArmMonitorLib|ArmVirtPkg/Library/ArmVirtMonitorLib/ArmVirtMonitorLib.inf + DxeImageVerificationLib|SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.inf [LibraryClasses.AARCH64] ArmPlatformLib|ArmVirtPkg/Library/ArmPlatformLibQemu/ArmPlatformLibQemu.inf @@ -389,7 +390,7 @@ !if $(SECURE_BOOT_ENABLE) == TRUE MdeModulePkg/Universal/SecurityStubDxe/SecurityStubDxe.inf { - NULL|SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.inf + NULL|SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationHandler.inf !if $(TPM2_ENABLE) == TRUE NULL|SecurityPkg/Library/DxeTpm2MeasureBootLib/DxeTpm2MeasureBootLib.inf !endif diff --git a/ArmVirtPkg/ArmVirtQemuKernel.dsc b/ArmVirtPkg/ArmVirtQemuKernel.dsc index 61d2536123..7ed62e2ea8 100644 --- a/ArmVirtPkg/ArmVirtQemuKernel.dsc +++ b/ArmVirtPkg/ArmVirtQemuKernel.dsc @@ -85,6 +85,7 @@ TpmPlatformHierarchyLib|SecurityPkg/Library/PeiDxeTpmPlatformHierarchyLibNull/PeiDxeTpmPlatformHierarchyLib.inf ArmMonitorLib|ArmVirtPkg/Library/ArmVirtMonitorLib/ArmVirtMonitorLib.inf + DxeImageVerificationLib|SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.inf [LibraryClasses.common.DXE_DRIVER] AcpiPlatformLib|OvmfPkg/Library/AcpiPlatformLib/DxeAcpiPlatformLib.inf @@ -311,7 +312,7 @@ !if $(SECURE_BOOT_ENABLE) == TRUE MdeModulePkg/Universal/SecurityStubDxe/SecurityStubDxe.inf { - NULL|SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.inf + NULL|SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationHandler.inf } SecurityPkg/VariableAuthenticated/SecureBootConfigDxe/SecureBootConfigDxe.inf OvmfPkg/EnrollDefaultKeys/EnrollDefaultKeys.inf diff --git a/DasharoPayloadPkg/DasharoPayloadPkg.dsc b/DasharoPayloadPkg/DasharoPayloadPkg.dsc index 9bfb1c3b7e..e83826e42d 100644 --- a/DasharoPayloadPkg/DasharoPayloadPkg.dsc +++ b/DasharoPayloadPkg/DasharoPayloadPkg.dsc @@ -363,6 +363,8 @@ AuthVariableLib|MdeModulePkg/Library/AuthVariableLibNull/AuthVariableLibNull.inf !endif + DxeImageVerificationLib|SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.inf + # # SMMSTORE # @@ -770,7 +772,7 @@ OrderedCollectionLib|MdePkg/Library/BaseOrderedCollectionRedBlackTreeLib/BaseOrd MdeModulePkg/Universal/SecurityStubDxe/SecurityStubDxe.inf { !if $(SECURE_BOOT_ENABLE) == TRUE - NULL|SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.inf + NULL|SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationHandler.inf !endif !if $(TPM_ENABLE) == TRUE NULL|SecurityPkg/Library/DxeTpmMeasureBootLib/DxeTpmMeasureBootLib.inf diff --git a/EmulatorPkg/EmulatorPkg.dsc b/EmulatorPkg/EmulatorPkg.dsc index 5bba746a02..452bac47fa 100644 --- a/EmulatorPkg/EmulatorPkg.dsc +++ b/EmulatorPkg/EmulatorPkg.dsc @@ -141,6 +141,7 @@ !else AuthVariableLib|MdeModulePkg/Library/AuthVariableLibNull/AuthVariableLibNull.inf !endif + DxeImageVerificationLib|SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.inf [LibraryClasses.common.SEC] PeiServicesLib|EmulatorPkg/Library/SecPeiServicesLib/SecPeiServicesLib.inf @@ -393,7 +394,7 @@ MdeModulePkg/Universal/SecurityStubDxe/SecurityStubDxe.inf { !if $(SECURE_BOOT_ENABLE) == TRUE - NULL|SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.inf + NULL|SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationHandler.inf !endif } diff --git a/OvmfPkg/Bhyve/BhyveX64.dsc b/OvmfPkg/Bhyve/BhyveX64.dsc index 5aab790316..7d5585aead 100644 --- a/OvmfPkg/Bhyve/BhyveX64.dsc +++ b/OvmfPkg/Bhyve/BhyveX64.dsc @@ -218,6 +218,7 @@ VariablePolicyLib|MdeModulePkg/Library/VariablePolicyLib/VariablePolicyLib.inf VariablePolicyHelperLib|MdeModulePkg/Library/VariablePolicyHelperLib/VariablePolicyHelperLib.inf VariableFlashInfoLib|MdeModulePkg/Library/BaseVariableFlashInfoLib/BaseVariableFlashInfoLib.inf + DxeImageVerificationLib|SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.inf # # Network libraries @@ -647,7 +648,7 @@ MdeModulePkg/Universal/SecurityStubDxe/SecurityStubDxe.inf { !if $(SECURE_BOOT_ENABLE) == TRUE - NULL|SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.inf + NULL|SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationHandler.inf !endif !include OvmfPkg/Include/Dsc/OvmfTpmSecurityStub.dsc.inc } diff --git a/OvmfPkg/CloudHv/CloudHvX64.dsc b/OvmfPkg/CloudHv/CloudHvX64.dsc index 1223e6f6a1..aa00e80100 100644 --- a/OvmfPkg/CloudHv/CloudHvX64.dsc +++ b/OvmfPkg/CloudHv/CloudHvX64.dsc @@ -231,7 +231,7 @@ VariablePolicyLib|MdeModulePkg/Library/VariablePolicyLib/VariablePolicyLib.inf VariablePolicyHelperLib|MdeModulePkg/Library/VariablePolicyHelperLib/VariablePolicyHelperLib.inf VariableFlashInfoLib|MdeModulePkg/Library/BaseVariableFlashInfoLib/BaseVariableFlashInfoLib.inf - + DxeImageVerificationLib|SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.inf # # Network libraries @@ -733,7 +733,7 @@ MdeModulePkg/Universal/SecurityStubDxe/SecurityStubDxe.inf { !if $(SECURE_BOOT_ENABLE) == TRUE - NULL|SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.inf + NULL|SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationHandler.inf !endif !include OvmfPkg/Include/Dsc/OvmfTpmSecurityStub.dsc.inc } diff --git a/OvmfPkg/IntelTdx/IntelTdxX64.dsc b/OvmfPkg/IntelTdx/IntelTdxX64.dsc index 6d3e0a5f1c..d29a47f21a 100644 --- a/OvmfPkg/IntelTdx/IntelTdxX64.dsc +++ b/OvmfPkg/IntelTdx/IntelTdxX64.dsc @@ -208,6 +208,7 @@ Tcg2PhysicalPresenceLib|OvmfPkg/Library/Tcg2PhysicalPresenceLibNull/DxeTcg2PhysicalPresenceLib.inf TpmMeasurementLib|SecurityPkg/Library/DxeTpmMeasurementLib/DxeTpmMeasurementLib.inf + DxeImageVerificationLib|SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.inf !include OvmfPkg/Include/Dsc/ShellLibs.dsc.inc @@ -570,7 +571,7 @@ MdeModulePkg/Universal/SecurityStubDxe/SecurityStubDxe.inf { !if $(SECURE_BOOT_ENABLE) == TRUE - NULL|SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.inf + NULL|SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationHandler.inf !endif NULL|SecurityPkg/Library/DxeTpm2MeasureBootLib/DxeTpm2MeasureBootLib.inf } diff --git a/OvmfPkg/Microvm/MicrovmX64.dsc b/OvmfPkg/Microvm/MicrovmX64.dsc index 84d587d069..0fcdb256f6 100644 --- a/OvmfPkg/Microvm/MicrovmX64.dsc +++ b/OvmfPkg/Microvm/MicrovmX64.dsc @@ -226,7 +226,7 @@ VariablePolicyLib|MdeModulePkg/Library/VariablePolicyLib/VariablePolicyLib.inf VariablePolicyHelperLib|MdeModulePkg/Library/VariablePolicyHelperLib/VariablePolicyHelperLib.inf VariableFlashInfoLib|MdeModulePkg/Library/BaseVariableFlashInfoLib/BaseVariableFlashInfoLib.inf - + DxeImageVerificationLib|SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.inf # # Network libraries @@ -715,7 +715,7 @@ MdeModulePkg/Universal/SecurityStubDxe/SecurityStubDxe.inf { !if $(SECURE_BOOT_ENABLE) == TRUE - NULL|SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.inf + NULL|SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationHandler.inf !endif !include OvmfPkg/Include/Dsc/OvmfTpmSecurityStub.dsc.inc } diff --git a/OvmfPkg/OvmfPkgIa32.dsc b/OvmfPkg/OvmfPkgIa32.dsc index 09c196feb3..68e76bef98 100644 --- a/OvmfPkg/OvmfPkgIa32.dsc +++ b/OvmfPkg/OvmfPkgIa32.dsc @@ -229,6 +229,7 @@ VariablePolicyLib|MdeModulePkg/Library/VariablePolicyLib/VariablePolicyLib.inf VariablePolicyHelperLib|MdeModulePkg/Library/VariablePolicyHelperLib/VariablePolicyHelperLib.inf VariableFlashInfoLib|MdeModulePkg/Library/BaseVariableFlashInfoLib/BaseVariableFlashInfoLib.inf + DxeImageVerificationLib|SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.inf # # Network libraries @@ -739,7 +740,7 @@ MdeModulePkg/Universal/SecurityStubDxe/SecurityStubDxe.inf { !if $(SECURE_BOOT_ENABLE) == TRUE - NULL|SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.inf + NULL|SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationHandler.inf !endif !include OvmfPkg/Include/Dsc/OvmfTpmSecurityStub.dsc.inc } diff --git a/OvmfPkg/OvmfPkgIa32X64.dsc b/OvmfPkg/OvmfPkgIa32X64.dsc index 0ad6e5a333..7ddb4cda28 100644 --- a/OvmfPkg/OvmfPkgIa32X64.dsc +++ b/OvmfPkg/OvmfPkgIa32X64.dsc @@ -235,6 +235,7 @@ VariablePolicyHelperLib|MdeModulePkg/Library/VariablePolicyHelperLib/VariablePolicyHelperLib.inf VariableFlashInfoLib|MdeModulePkg/Library/BaseVariableFlashInfoLib/BaseVariableFlashInfoLib.inf BlParseLib|OvmfPkg/Library/QemuParseLib/QemuParseLib.inf + DxeImageVerificationLib|SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.inf # # Network libraries @@ -753,7 +754,7 @@ MdeModulePkg/Universal/SecurityStubDxe/SecurityStubDxe.inf { !if $(SECURE_BOOT_ENABLE) == TRUE - NULL|SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.inf + NULL|SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationHandler.inf !endif !include OvmfPkg/Include/Dsc/OvmfTpmSecurityStub.dsc.inc } diff --git a/OvmfPkg/OvmfPkgX64.dsc b/OvmfPkg/OvmfPkgX64.dsc index 0c9b48e46f..49959e43c4 100644 --- a/OvmfPkg/OvmfPkgX64.dsc +++ b/OvmfPkg/OvmfPkgX64.dsc @@ -279,6 +279,7 @@ DasharoVariablesLib|DasharoModulePkg/Library/DasharoVariablesLib/DasharoVariablesLib.inf BlParseLib|OvmfPkg/Library/QemuParseLib/QemuParseLib.inf + DxeImageVerificationLib|SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.inf # # Network libraries @@ -882,7 +883,7 @@ MdeModulePkg/Universal/SecurityStubDxe/SecurityStubDxe.inf { !if $(SECURE_BOOT_ENABLE) == TRUE - NULL|SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.inf + NULL|SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationHandler.inf !endif !include OvmfPkg/Include/Dsc/OvmfTpmSecurityStub.dsc.inc } diff --git a/OvmfPkg/RiscVVirt/RiscVVirtQemu.dsc b/OvmfPkg/RiscVVirt/RiscVVirtQemu.dsc index ca211b3db3..ae2dc7adaf 100644 --- a/OvmfPkg/RiscVVirt/RiscVVirtQemu.dsc +++ b/OvmfPkg/RiscVVirt/RiscVVirtQemu.dsc @@ -117,6 +117,7 @@ TpmMeasurementLib|MdeModulePkg/Library/TpmMeasurementLibNull/TpmMeasurementLibNull.inf TpmPlatformHierarchyLib|SecurityPkg/Library/PeiDxeTpmPlatformHierarchyLibNull/PeiDxeTpmPlatformHierarchyLib.inf !endif + DxeImageVerificationLib|SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.inf [LibraryClasses.common.DXE_DRIVER] AcpiPlatformLib|OvmfPkg/Library/AcpiPlatformLib/DxeAcpiPlatformLib.inf @@ -298,7 +299,7 @@ !if $(SECURE_BOOT_ENABLE) == TRUE MdeModulePkg/Universal/SecurityStubDxe/SecurityStubDxe.inf { - NULL|SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.inf + NULL|SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationHandler.inf !if $(TPM2_ENABLE) == TRUE NULL|SecurityPkg/Library/DxeTpm2MeasureBootLib/DxeTpm2MeasureBootLib.inf !endif diff --git a/SecurityPkg/Include/Library/DxeImageVerificationLib.h b/SecurityPkg/Include/Library/DxeImageVerificationLib.h new file mode 100644 index 0000000000..b18f2ea337 --- /dev/null +++ b/SecurityPkg/Include/Library/DxeImageVerificationLib.h @@ -0,0 +1,240 @@ +/** @file + The library header file, defines functions used by ImageVerificationLib. + +Copyright (c) 2009 - 2014, Intel Corporation. All rights reserved.
+SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef __DXEIMAGEVERIFICATIONLIB_H__ +#define __DXEIMAGEVERIFICATIONLIB_H__ + +#include +#include +#include +// +// Set max digest size as SHA512 Output (64 bytes) by far +// +#define MAX_DIGEST_SIZE SHA512_DIGEST_SIZE + +// +// Support hash types +// +#define HASHALG_SHA1 0x00000000 +#define HASHALG_SHA224 0x00000001 +#define HASHALG_SHA256 0x00000002 +#define HASHALG_SHA384 0x00000003 +#define HASHALG_SHA512 0x00000004 +#define HASHALG_MAX 0x00000005 +// Used only by SecureBootConfigDxe +#define HASHALG_RAW 0x00000006 + +// +// +// PKCS7 Certificate definition +// +typedef struct { + WIN_CERTIFICATE Hdr; + UINT8 CertData[1]; +} WIN_CERTIFICATE_EFI_PKCS; + +/** + Check whether the image signature is forbidden by the forbidden database (dbx). + The image is forbidden to load if any certificates for signing are revoked before signing time. + + @param[in] AuthData Pointer to the Authenticode signature retrieved from the signed image. + @param[in] AuthDataSize Size of the Authenticode signature in bytes. + @param[in] ImageDigest Buffer containing digest of the file. + @param[in] ImageDigestSize Size of the digest buffer. + + @retval TRUE Image is forbidden by dbx. + @retval FALSE Image is not forbidden by dbx. + +**/ +BOOLEAN +IsForbiddenByDbx ( + IN UINT8 *AuthData, + IN UINTN AuthDataSize, + IN UINT8 *ImageDigest, + IN UINTN ImageDigestSize + ); + +/** + Check whether the image signature can be verified by the trusted certificates in DB database. + + @param[in] AuthData Pointer to the Authenticode signature retrieved from signed image. + @param[in] AuthDataSize Size of the Authenticode signature in bytes. + @param[in] ImageDigest Buffer containing digest of the file. + @param[in] ImageDigestSize Size of the digest buffer. + + @retval TRUE Image passed verification using certificate in db. + @retval FALSE Image didn't pass verification using certificate in db. + +**/ +BOOLEAN +IsAllowedByDb ( + IN UINT8 *AuthData, + IN UINTN AuthDataSize, + IN UINT8 *ImageDigest, + IN UINTN ImageDigestSize + ); + +/** + Check whether signature is in specified database. + + @param[in] VariableName Name of database variable that is searched in. + @param[in] Signature Pointer to signature that is searched for. + @param[in] CertType Pointer to hash algorithm. + @param[in] SignatureSize Size of Signature. + @param[out] IsFound Search result. Only valid if EFI_SUCCESS returned + + @retval EFI_SUCCESS Finished the search without any error. + @retval Others Error occurred in the search of database. + +**/ +EFI_STATUS +IsSignatureFoundInDatabase ( + IN CHAR16 *VariableName, + IN UINT8 *Signature, + IN EFI_GUID *CertType, + IN UINTN SignatureSize, + OUT BOOLEAN *IsFound + ); + +/** + Check whether signature is in specified database. + + @param[in] FileBase Pointer to the file content. + @param[in] FileSize Size of the file. + @param[inout] SecDataDir POinter to a pointer to the Image Security Directory. + + @retval EFI_SUCCESS Parsed the image without any error. + @retval Others Error occurred in the image parsing. + +**/ +EFI_STATUS +GetImageSecDataDir ( + IN VOID *FileBase, + IN UINTN FileSize, + IN OUT EFI_IMAGE_DATA_DIRECTORY **SecDataDir + ); + +/** + Recognize the Hash algorithm in PE/COFF Authenticode and calculate hash of + Pe/Coff image based on the authenticode image hashing in PE/COFF Specification + 8.0 Appendix A + + Caution: This function may receive untrusted input. + PE/COFF image is external input, so this function will validate its data structure + within this image buffer before use. + + @param[in] ImageBase Pointer to the file content. + @param[in] ImageSize Size of the file. + @param[in] AuthData Pointer to the Authenticode Signature retrieved from signed image. + @param[in] AuthDataSize Size of the Authenticode Signature in bytes. + @param[out] ImageDigest Buffer with the file digest. + @param[out] ImageDigestSize Size of the file digest. + @param[out] CertType GUID representing the type of hash. + + @retval EFI_UNSUPPORTED Hash algorithm is not supported. + @retval EFI_SUCCESS Hash successfully. + +**/ +EFI_STATUS +HashPeImageByType ( + IN VOID *ImageBase, + IN UINTN ImageSize, + IN UINT8 *AuthData, + IN UINTN AuthDataSize, + OUT UINT8 *ImageDigest, + OUT UINTN *ImageDigestSize, + OUT EFI_GUID *CertType + ); + +/** + Calculate hash of Pe/Coff image based on the authenticode image hashing in + PE/COFF Specification 8.0 Appendix A + + Caution: This function may receive untrusted input. + PE/COFF image is external input, so this function will validate its data structure + within this image buffer before use. + + @param[in] ImageBase Address to the file content. + @param[in] ImageSize File size. + @param[in] HashAlg Hash algorithm type. + @param[out] ImageDigest Buffer containing digest of the file. + @param[out] ImageDigestSize Size of the digest buffer. + @param[out] CertType GUID representing the hash type + + @retval TRUE Successfully hash image. + @retval FALSE Fail in hash image. + +**/ +BOOLEAN +HashPeImage ( + IN VOID* ImageBase, + IN UINTN ImageSize, + IN UINT32 HashAlg, + OUT UINT8 *ImageDigest, + OUT UINTN *ImageDigestSize, + OUT EFI_GUID *CertType + ); + +/** + Check whether the hash of an given X.509 certificate is in forbidden database (DBX). + + @param[in] Certificate Pointer to X.509 Certificate that is searched for. + @param[in] CertSize Size of X.509 Certificate. + @param[in] SignatureList Pointer to the Signature List in forbidden database. + @param[in] SignatureListSize Size of Signature List. + @param[out] RevocationTime Return the time that the certificate was revoked. + @param[out] IsFound Search result. Only valid if EFI_SUCCESS returned. + + @retval EFI_SUCCESS Finished the search without any error. + @retval Others Error occurred in the search of database. + +**/ +EFI_STATUS +IsCertHashFoundInDbx ( + IN UINT8 *Certificate, + IN UINTN CertSize, + IN EFI_SIGNATURE_LIST *SignatureList, + IN UINTN SignatureListSize, + OUT EFI_TIME *RevocationTime, + OUT BOOLEAN *IsFound + ); + +/** + Calculate the hash of a certificate data with the specified hash algorithm. + + @param[in] CertData The certificate data to be hashed. + @param[in] CertSize The certificate size in bytes. + @param[in] HashAlg The specified hash algorithm. + @param[out] CertHash The output digest of the certificate + + @retval TRUE Successfully got the hash of the CertData. + @retval FALSE Failed to get the hash of CertData. + +**/ +BOOLEAN +CalculateCertHash ( + IN UINT8 *CertData, + IN UINTN CertSize, + IN UINT32 HashAlg, + OUT UINT8 *CertHash + ); + +/** + Get the length of the digest of given hash algorithm. + + @param[in] HashAlg Hash algorithm type. + + @retval UINTN Length of the digest of given hash algorithm. + +**/ +UINTN +GetDigestLength ( + IN UINT32 HashAlg + ); + +#endif diff --git a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationHandler.c b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationHandler.c new file mode 100644 index 0000000000..741ed0ea88 --- /dev/null +++ b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationHandler.c @@ -0,0 +1,774 @@ +/** @file + Implement image verification services for secure boot service + +Copyright (c) 2009 - 2018, Intel Corporation. All rights reserved.
+(C) Copyright 2016 Hewlett Packard Enterprise Development LP
+SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "ImageVerificationLibInternal.h" + +/** + Get the image type. + + @param[in] File This is a pointer to the device path of the file that is + being dispatched. + + @return UINT32 Image Type + +**/ +UINT32 +GetImageType ( + IN CONST EFI_DEVICE_PATH_PROTOCOL *File + ) +{ + EFI_STATUS Status; + EFI_HANDLE DeviceHandle; + EFI_DEVICE_PATH_PROTOCOL *TempDevicePath; + EFI_BLOCK_IO_PROTOCOL *BlockIo; + + if (File == NULL) { + return IMAGE_UNKNOWN; + } + + // + // First check to see if File is from a Firmware Volume + // + DeviceHandle = NULL; + TempDevicePath = (EFI_DEVICE_PATH_PROTOCOL *)File; + Status = gBS->LocateDevicePath ( + &gEfiFirmwareVolume2ProtocolGuid, + &TempDevicePath, + &DeviceHandle + ); + if (!EFI_ERROR (Status)) { + Status = gBS->OpenProtocol ( + DeviceHandle, + &gEfiFirmwareVolume2ProtocolGuid, + NULL, + NULL, + NULL, + EFI_OPEN_PROTOCOL_TEST_PROTOCOL + ); + if (!EFI_ERROR (Status)) { + return IMAGE_FROM_FV; + } + } + + // + // Next check to see if File is from a Block I/O device + // + DeviceHandle = NULL; + TempDevicePath = (EFI_DEVICE_PATH_PROTOCOL *)File; + Status = gBS->LocateDevicePath ( + &gEfiBlockIoProtocolGuid, + &TempDevicePath, + &DeviceHandle + ); + if (!EFI_ERROR (Status)) { + BlockIo = NULL; + Status = gBS->OpenProtocol ( + DeviceHandle, + &gEfiBlockIoProtocolGuid, + (VOID **)&BlockIo, + NULL, + NULL, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + if (!EFI_ERROR (Status) && (BlockIo != NULL)) { + if (BlockIo->Media != NULL) { + if (BlockIo->Media->RemovableMedia) { + // + // Block I/O is present and specifies the media is removable + // + return IMAGE_FROM_REMOVABLE_MEDIA; + } else { + // + // Block I/O is present and specifies the media is not removable + // + return IMAGE_FROM_FIXED_MEDIA; + } + } + } + } + + // + // File is not in a Firmware Volume or on a Block I/O device, so check to see if + // the device path supports the Simple File System Protocol. + // + DeviceHandle = NULL; + TempDevicePath = (EFI_DEVICE_PATH_PROTOCOL *)File; + Status = gBS->LocateDevicePath ( + &gEfiSimpleFileSystemProtocolGuid, + &TempDevicePath, + &DeviceHandle + ); + if (!EFI_ERROR (Status)) { + // + // Simple File System is present without Block I/O, so assume media is fixed. + // + return IMAGE_FROM_FIXED_MEDIA; + } + + // + // File is not from an FV, Block I/O or Simple File System, so the only options + // left are a PCI Option ROM and a Load File Protocol such as a PXE Boot from a NIC. + // + TempDevicePath = (EFI_DEVICE_PATH_PROTOCOL *)File; + while (!IsDevicePathEndType (TempDevicePath)) { + switch (DevicePathType (TempDevicePath)) { + case MEDIA_DEVICE_PATH: + if (DevicePathSubType (TempDevicePath) == MEDIA_RELATIVE_OFFSET_RANGE_DP) { + return IMAGE_FROM_OPTION_ROM; + } + + break; + + case MESSAGING_DEVICE_PATH: + if (DevicePathSubType (TempDevicePath) == MSG_MAC_ADDR_DP) { + return IMAGE_FROM_REMOVABLE_MEDIA; + } + + break; + + default: + break; + } + + TempDevicePath = NextDevicePathNode (TempDevicePath); + } + + return IMAGE_UNKNOWN; +} + + +/** + Returns the size of a given image execution info table in bytes. + + This function returns the size, in bytes, of the image execution info table specified by + ImageExeInfoTable. If ImageExeInfoTable is NULL, then 0 is returned. + + @param ImageExeInfoTable A pointer to a image execution info table structure. + + @retval 0 If ImageExeInfoTable is NULL. + @retval Others The size of a image execution info table in bytes. + +**/ +UINTN +GetImageExeInfoTableSize ( + EFI_IMAGE_EXECUTION_INFO_TABLE *ImageExeInfoTable + ) +{ + UINTN Index; + EFI_IMAGE_EXECUTION_INFO *ImageExeInfoItem; + UINTN TotalSize; + + if (ImageExeInfoTable == NULL) { + return 0; + } + + ImageExeInfoItem = (EFI_IMAGE_EXECUTION_INFO *)((UINT8 *)ImageExeInfoTable + sizeof (EFI_IMAGE_EXECUTION_INFO_TABLE)); + TotalSize = sizeof (EFI_IMAGE_EXECUTION_INFO_TABLE); + for (Index = 0; Index < ImageExeInfoTable->NumberOfImages; Index++) { + TotalSize += ReadUnaligned32 ((UINT32 *)&ImageExeInfoItem->InfoSize); + ImageExeInfoItem = (EFI_IMAGE_EXECUTION_INFO *)((UINT8 *)ImageExeInfoItem + ReadUnaligned32 ((UINT32 *)&ImageExeInfoItem->InfoSize)); + } + + return TotalSize; +} + +/** + Create an Image Execution Information Table entry and add it to system configuration table. + + @param[in] Action Describes the action taken by the firmware regarding this image. + @param[in] Name Input a null-terminated, user-friendly name. + @param[in] DevicePath Input device path pointer. + @param[in] Signature Input signature info in EFI_SIGNATURE_LIST data structure. + @param[in] SignatureSize Size of signature. Must be zero if Signature is NULL. + +**/ +VOID +AddImageExeInfo ( + IN EFI_IMAGE_EXECUTION_ACTION Action, + IN CHAR16 *Name OPTIONAL, + IN CONST EFI_DEVICE_PATH_PROTOCOL *DevicePath, + IN EFI_SIGNATURE_LIST *Signature OPTIONAL, + IN UINTN SignatureSize + ) +{ + EFI_IMAGE_EXECUTION_INFO_TABLE *ImageExeInfoTable; + EFI_IMAGE_EXECUTION_INFO_TABLE *NewImageExeInfoTable; + EFI_IMAGE_EXECUTION_INFO *ImageExeInfoEntry; + UINTN ImageExeInfoTableSize; + UINTN NewImageExeInfoEntrySize; + UINTN NameStringLen; + UINTN DevicePathSize; + CHAR16 *NameStr; + + ImageExeInfoTable = NULL; + NewImageExeInfoTable = NULL; + ImageExeInfoEntry = NULL; + NameStringLen = 0; + NameStr = NULL; + + if (DevicePath == NULL) { + return; + } + + if (Name != NULL) { + NameStringLen = StrSize (Name); + } else { + NameStringLen = sizeof (CHAR16); + } + + EfiGetSystemConfigurationTable (&gEfiImageSecurityDatabaseGuid, (VOID **)&ImageExeInfoTable); + if (ImageExeInfoTable != NULL) { + // + // The table has been found! + // We must enlarge the table to accommodate the new exe info entry. + // + ImageExeInfoTableSize = GetImageExeInfoTableSize (ImageExeInfoTable); + } else { + // + // Not Found! + // We should create a new table to append to the configuration table. + // + ImageExeInfoTableSize = sizeof (EFI_IMAGE_EXECUTION_INFO_TABLE); + } + + DevicePathSize = GetDevicePathSize (DevicePath); + + // + // Signature size can be odd. Pad after signature to ensure next EXECUTION_INFO entry align + // + ASSERT (Signature != NULL || SignatureSize == 0); + NewImageExeInfoEntrySize = sizeof (EFI_IMAGE_EXECUTION_INFO) + NameStringLen + DevicePathSize + SignatureSize; + + NewImageExeInfoTable = (EFI_IMAGE_EXECUTION_INFO_TABLE *)AllocateRuntimePool (ImageExeInfoTableSize + NewImageExeInfoEntrySize); + if (NewImageExeInfoTable == NULL) { + return; + } + + if (ImageExeInfoTable != NULL) { + CopyMem (NewImageExeInfoTable, ImageExeInfoTable, ImageExeInfoTableSize); + } else { + NewImageExeInfoTable->NumberOfImages = 0; + } + + NewImageExeInfoTable->NumberOfImages++; + ImageExeInfoEntry = (EFI_IMAGE_EXECUTION_INFO *)((UINT8 *)NewImageExeInfoTable + ImageExeInfoTableSize); + // + // Update new item's information. + // + WriteUnaligned32 ((UINT32 *)ImageExeInfoEntry, Action); + WriteUnaligned32 ((UINT32 *)((UINT8 *)ImageExeInfoEntry + sizeof (EFI_IMAGE_EXECUTION_ACTION)), (UINT32)NewImageExeInfoEntrySize); + + NameStr = (CHAR16 *)(ImageExeInfoEntry + 1); + if (Name != NULL) { + CopyMem ((UINT8 *)NameStr, Name, NameStringLen); + } else { + ZeroMem ((UINT8 *)NameStr, sizeof (CHAR16)); + } + + CopyMem ( + (UINT8 *)NameStr + NameStringLen, + DevicePath, + DevicePathSize + ); + if (Signature != NULL) { + CopyMem ( + (UINT8 *)NameStr + NameStringLen + DevicePathSize, + Signature, + SignatureSize + ); + } + + // + // Update/replace the image execution table. + // + gBS->InstallConfigurationTable (&gEfiImageSecurityDatabaseGuid, (VOID *)NewImageExeInfoTable); + + // + // Free Old table data! + // + if (ImageExeInfoTable != NULL) { + FreePool (ImageExeInfoTable); + } +} + +/** + Provide verification service for signed images, which include both signature validation + and platform policy control. For signature types, both UEFI WIN_CERTIFICATE_UEFI_GUID and + MSFT Authenticode type signatures are supported. + + In this implementation, only verify external executables when in USER MODE. + Executables from FV is bypass, so pass in AuthenticationStatus is ignored. + + The image verification policy is: + If the image is signed, + At least one valid signature or at least one hash value of the image must match a record + in the security database "db", and no valid signature nor any hash value of the image may + be reflected in the security database "dbx". + Otherwise, the image is not signed, + The hash value of the image must match a record in the security database "db", and + not be reflected in the security data base "dbx". + + Caution: This function may receive untrusted input. + PE/COFF image is external input, so this function will validate its data structure + within this image buffer before use. + + @param[in] AuthenticationStatus + This is the authentication status returned from the security + measurement services for the input file. + @param[in] File This is a pointer to the device path of the file that is + being dispatched. This will optionally be used for logging. + @param[in] FileBuffer File buffer matches the input file device path. + @param[in] FileSize Size of File buffer matches the input file device path. + @param[in] BootPolicy A boot policy that was used to call LoadImage() UEFI service. + + @retval EFI_SUCCESS The file specified by DevicePath and non-NULL + FileBuffer did authenticate, and the platform policy dictates + that the DXE Foundation may use the file. + @retval EFI_SUCCESS The device path specified by NULL device path DevicePath + and non-NULL FileBuffer did authenticate, and the platform + policy dictates that the DXE Foundation may execute the image in + FileBuffer. + @retval EFI_SECURITY_VIOLATION The file specified by File did not authenticate, and + the platform policy dictates that File should be placed + in the untrusted state. The image has been added to the file + execution table. + @retval EFI_ACCESS_DENIED The file specified by File and FileBuffer did not + authenticate, and the platform policy dictates that the DXE + Foundation may not use File. The image has + been added to the file execution table. + +**/ +EFI_STATUS +EFIAPI +DxeImageVerificationHandler ( + IN UINT32 AuthenticationStatus, + IN CONST EFI_DEVICE_PATH_PROTOCOL *File OPTIONAL, + IN VOID *FileBuffer, + IN UINTN FileSize, + IN BOOLEAN BootPolicy + ) +{ + BOOLEAN IsVerified; + EFI_SIGNATURE_LIST *SignatureList; + UINTN SignatureListSize; + EFI_SIGNATURE_DATA *Signature; + EFI_IMAGE_EXECUTION_ACTION Action; + WIN_CERTIFICATE *WinCertificate; + UINT32 Policy; + UINT8 SecureBoot; + UINTN SecureBootSize; + WIN_CERTIFICATE_EFI_PKCS *PkcsCertData; + WIN_CERTIFICATE_UEFI_GUID *WinCertUefiGuid; + UINT8 *AuthData; + UINTN AuthDataSize; + EFI_IMAGE_DATA_DIRECTORY *SecDataDir; + UINT32 SecDataDirEnd; + UINT32 SecDataDirLeft; + UINT32 OffSet; + CHAR16 *NameStr; + EFI_STATUS HashStatus; + EFI_STATUS DbStatus; + EFI_STATUS VarStatus; + EFI_STATUS ImageStatus; + UINT32 VarAttr; + BOOLEAN IsFound; + UINT8 HashAlg; + BOOLEAN IsFoundInDatabase; + UINT8 ImageDigest[MAX_DIGEST_SIZE]; + UINTN ImageDigestSize; + EFI_GUID CertType; + + SignatureList = NULL; + SignatureListSize = 0; + WinCertificate = NULL; + SecDataDir = NULL; + PkcsCertData = NULL; + Action = EFI_IMAGE_EXECUTION_AUTH_UNTESTED; + IsVerified = FALSE; + IsFound = FALSE; + IsFoundInDatabase = FALSE; + + // + // Check the image type and get policy setting. + // + switch (GetImageType (File)) { + case IMAGE_FROM_FV: + Policy = ALWAYS_EXECUTE; + break; + + case IMAGE_FROM_OPTION_ROM: + Policy = PcdGet32 (PcdOptionRomImageVerificationPolicy); + break; + + case IMAGE_FROM_REMOVABLE_MEDIA: + Policy = PcdGet32 (PcdRemovableMediaImageVerificationPolicy); + break; + + case IMAGE_FROM_FIXED_MEDIA: + Policy = PcdGet32 (PcdFixedMediaImageVerificationPolicy); + break; + + default: + Policy = DENY_EXECUTE_ON_SECURITY_VIOLATION; + break; + } + + // + // If policy is always/never execute, return directly. + // + if (Policy == ALWAYS_EXECUTE) { + return EFI_SUCCESS; + } + + if (Policy == NEVER_EXECUTE) { + return EFI_ACCESS_DENIED; + } + + // + // The policy QUERY_USER_ON_SECURITY_VIOLATION and ALLOW_EXECUTE_ON_SECURITY_VIOLATION + // violates the UEFI spec and has been removed. + // + ASSERT (Policy != QUERY_USER_ON_SECURITY_VIOLATION && Policy != ALLOW_EXECUTE_ON_SECURITY_VIOLATION); + if ((Policy == QUERY_USER_ON_SECURITY_VIOLATION) || (Policy == ALLOW_EXECUTE_ON_SECURITY_VIOLATION)) { + CpuDeadLoop (); + } + + SecureBootSize = sizeof (SecureBoot); + VarStatus = gRT->GetVariable (EFI_SECURE_BOOT_MODE_NAME, &gEfiGlobalVariableGuid, &VarAttr, &SecureBootSize, &SecureBoot); + // + // Skip verification if SecureBoot variable doesn't exist. + // + if (VarStatus == EFI_NOT_FOUND) { + return EFI_SUCCESS; + } + + // + // Skip verification if SecureBoot is disabled but not AuditMode + // + if ((VarStatus == EFI_SUCCESS) && + (VarAttr == (EFI_VARIABLE_BOOTSERVICE_ACCESS | + EFI_VARIABLE_RUNTIME_ACCESS)) && + (SecureBoot == SECURE_BOOT_MODE_DISABLE)) + { + return EFI_SUCCESS; + } + + // + // Read the Dos header. + // + if (FileBuffer == NULL) { + return EFI_ACCESS_DENIED; + } + + ImageStatus = GetImageSecDataDir (FileBuffer, FileSize, &SecDataDir); + if (EFI_ERROR (ImageStatus)) { + goto Failed; + } + + // + // Start Image Validation. + // + if ((SecDataDir == NULL) || (SecDataDir->Size == 0)) { + // + // This image is not signed. The hash value of the image must match a record in the security database "db", + // and not be reflected in the security data base "dbx". + // + HashAlg = HASHALG_MAX; + while (HashAlg > 0) { + HashAlg--; + + if (!HashPeImage (FileBuffer, FileSize, HashAlg, ImageDigest, &ImageDigestSize, &CertType)) { + continue; + } + + DbStatus = IsSignatureFoundInDatabase ( + EFI_IMAGE_SECURITY_DATABASE1, + ImageDigest, + &CertType, + ImageDigestSize, + &IsFound + ); + if (EFI_ERROR (DbStatus) || IsFound) { + // + // Image Hash is in forbidden database (DBX). + // + DEBUG ((DEBUG_INFO, "DxeImageVerificationLib: Image is not signed and hash of image is forbidden by DBX.\n")); + goto Failed; + } + + DbStatus = IsSignatureFoundInDatabase ( + EFI_IMAGE_SECURITY_DATABASE, + ImageDigest, + &CertType, + ImageDigestSize, + &IsFound + ); + if (!EFI_ERROR (DbStatus) && IsFound) { + // + // Image Hash is in allowed database (DB). + // + IsFoundInDatabase = TRUE; + } + } + + if (IsFoundInDatabase) { + return EFI_SUCCESS; + } + + // + // Image Hash is not found in both forbidden and allowed database. + // + DEBUG ((DEBUG_INFO, "DxeImageVerificationLib: Image is not signed and hash of image is not found in DB/DBX.\n")); + goto Failed; + } + + // + // Verify the signature of the image, multiple signatures are allowed as per PE/COFF Section 4.7 + // "Attribute Certificate Table". + // The first certificate starts at offset (SecDataDir->VirtualAddress) from the start of the file. + // + SecDataDirEnd = SecDataDir->VirtualAddress + SecDataDir->Size; + for (OffSet = SecDataDir->VirtualAddress; + OffSet < SecDataDirEnd; + OffSet += (WinCertificate->dwLength + ALIGN_SIZE (WinCertificate->dwLength))) + { + SecDataDirLeft = SecDataDirEnd - OffSet; + if (SecDataDirLeft <= sizeof (WIN_CERTIFICATE)) { + break; + } + + WinCertificate = (WIN_CERTIFICATE *)(FileBuffer + OffSet); + if ((SecDataDirLeft < WinCertificate->dwLength) || + (SecDataDirLeft - WinCertificate->dwLength < + ALIGN_SIZE (WinCertificate->dwLength))) + { + break; + } + + // + // Verify the image's Authenticode signature, only DER-encoded PKCS#7 signed data is supported. + // + if (WinCertificate->wCertificateType == WIN_CERT_TYPE_PKCS_SIGNED_DATA) { + // + // The certificate is formatted as WIN_CERTIFICATE_EFI_PKCS which is described in the + // Authenticode specification. + // + PkcsCertData = (WIN_CERTIFICATE_EFI_PKCS *)WinCertificate; + if (PkcsCertData->Hdr.dwLength <= sizeof (PkcsCertData->Hdr)) { + break; + } + + AuthData = PkcsCertData->CertData; + AuthDataSize = PkcsCertData->Hdr.dwLength - sizeof (PkcsCertData->Hdr); + } else if (WinCertificate->wCertificateType == WIN_CERT_TYPE_EFI_GUID) { + // + // The certificate is formatted as WIN_CERTIFICATE_UEFI_GUID which is described in UEFI Spec. + // + WinCertUefiGuid = (WIN_CERTIFICATE_UEFI_GUID *)WinCertificate; + if (WinCertUefiGuid->Hdr.dwLength <= OFFSET_OF (WIN_CERTIFICATE_UEFI_GUID, CertData)) { + break; + } + + if (!CompareGuid (&WinCertUefiGuid->CertType, &gEfiCertPkcs7Guid)) { + continue; + } + + AuthData = WinCertUefiGuid->CertData; + AuthDataSize = WinCertUefiGuid->Hdr.dwLength - OFFSET_OF (WIN_CERTIFICATE_UEFI_GUID, CertData); + } else { + if (WinCertificate->dwLength < sizeof (WIN_CERTIFICATE)) { + break; + } + + continue; + } + + HashStatus = HashPeImageByType ( + FileBuffer, + FileSize, + AuthData, + AuthDataSize, + ImageDigest, + &ImageDigestSize, + &CertType); + if (EFI_ERROR (HashStatus)) { + continue; + } + + // + // Check the digital signature against the revoked certificate in forbidden database (dbx). + // + if (IsForbiddenByDbx (AuthData, AuthDataSize, ImageDigest, ImageDigestSize)) { + Action = EFI_IMAGE_EXECUTION_AUTH_SIG_FAILED; + IsVerified = FALSE; + break; + } + + // + // Check the digital signature against the valid certificate in allowed database (db). + // + if (!IsVerified) { + if (IsAllowedByDb (AuthData, AuthDataSize, ImageDigest, ImageDigestSize)) { + IsVerified = TRUE; + } + } + + // + // Check the image's hash value. + // + DbStatus = IsSignatureFoundInDatabase ( + EFI_IMAGE_SECURITY_DATABASE1, + ImageDigest, + &CertType, + ImageDigestSize, + &IsFound + ); + if (EFI_ERROR (DbStatus) || IsFound) { + Action = EFI_IMAGE_EXECUTION_AUTH_SIG_FOUND; + DEBUG ((DEBUG_INFO, "DxeImageVerificationLib: Image is signed but hash of image is found in DBX.\n")); + IsVerified = FALSE; + break; + } + + if (!IsVerified) { + DbStatus = IsSignatureFoundInDatabase ( + EFI_IMAGE_SECURITY_DATABASE, + ImageDigest, + &CertType, + ImageDigestSize, + &IsFound + ); + if (!EFI_ERROR (DbStatus) && IsFound) { + IsVerified = TRUE; + } else { + Action = EFI_IMAGE_EXECUTION_AUTH_SIG_NOT_FOUND; + DEBUG ((DEBUG_INFO, "DxeImageVerificationLib: Image is signed but signature is not allowed by DB and hash of image is not found in DB/DBX.\n")); + } + } + } + + if (OffSet != SecDataDirEnd) { + // + // The Size in Certificate Table or the attribute certificate table is corrupted. + // + IsVerified = FALSE; + } + + if (IsVerified) { + return EFI_SUCCESS; + } + + if ((Action == EFI_IMAGE_EXECUTION_AUTH_SIG_FAILED) || (Action == EFI_IMAGE_EXECUTION_AUTH_SIG_FOUND)) { + // + // Get image hash value as signature of executable. + // + SignatureListSize = sizeof (EFI_SIGNATURE_LIST) + sizeof (EFI_SIGNATURE_DATA) - 1 + ImageDigestSize; + SignatureList = (EFI_SIGNATURE_LIST *)AllocateZeroPool (SignatureListSize); + if (SignatureList == NULL) { + SignatureListSize = 0; + goto Failed; + } + + SignatureList->SignatureHeaderSize = 0; + SignatureList->SignatureListSize = (UINT32)SignatureListSize; + SignatureList->SignatureSize = (UINT32)(sizeof (EFI_SIGNATURE_DATA) - 1 + ImageDigestSize); + CopyMem (&SignatureList->SignatureType, &CertType, sizeof (EFI_GUID)); + Signature = (EFI_SIGNATURE_DATA *)((UINT8 *)SignatureList + sizeof (EFI_SIGNATURE_LIST)); + CopyMem (Signature->SignatureData, ImageDigest, ImageDigestSize); + } + +Failed: + // + // Policy decides to defer or reject the image; add its information in image + // executable information table in either case. + // + NameStr = ConvertDevicePathToText (File, FALSE, TRUE); + AddImageExeInfo (Action, NameStr, File, SignatureList, SignatureListSize); + if (NameStr != NULL) { + DEBUG ((DEBUG_INFO, "The image doesn't pass verification: %s\n", NameStr)); + FreePool (NameStr); + } + + if (SignatureList != NULL) { + FreePool (SignatureList); + } + + if (Policy == DEFER_EXECUTE_ON_SECURITY_VIOLATION) { + return EFI_SECURITY_VIOLATION; + } + + return EFI_ACCESS_DENIED; +} + +/** + On Ready To Boot Services Event notification handler. + + Add the image execution information table if it is not in system configuration table. + + @param[in] Event Event whose notification function is being invoked + @param[in] Context Pointer to the notification function's context + +**/ +VOID +EFIAPI +OnReadyToBoot ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + EFI_IMAGE_EXECUTION_INFO_TABLE *ImageExeInfoTable; + UINTN ImageExeInfoTableSize; + + EfiGetSystemConfigurationTable (&gEfiImageSecurityDatabaseGuid, (VOID **)&ImageExeInfoTable); + if (ImageExeInfoTable != NULL) { + return; + } + + ImageExeInfoTableSize = sizeof (EFI_IMAGE_EXECUTION_INFO_TABLE); + ImageExeInfoTable = (EFI_IMAGE_EXECUTION_INFO_TABLE *)AllocateRuntimePool (ImageExeInfoTableSize); + if (ImageExeInfoTable == NULL) { + return; + } + + ImageExeInfoTable->NumberOfImages = 0; + gBS->InstallConfigurationTable (&gEfiImageSecurityDatabaseGuid, (VOID *)ImageExeInfoTable); +} + +/** + Register security measurement handler. + + @param ImageHandle ImageHandle of the loaded driver. + @param SystemTable Pointer to the EFI System Table. + + @retval EFI_SUCCESS The handlers were registered successfully. +**/ +EFI_STATUS +EFIAPI +DxeImageVerificationLibConstructor ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + EFI_EVENT Event; + + // + // Register the event to publish the image execution table. + // + EfiCreateEventReadyToBootEx ( + TPL_CALLBACK, + OnReadyToBoot, + NULL, + &Event + ); + + return RegisterSecurity2Handler ( + DxeImageVerificationHandler, + EFI_AUTH_OPERATION_VERIFY_IMAGE | EFI_AUTH_OPERATION_IMAGE_REQUIRED + ); +} diff --git a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationHandler.inf b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationHandler.inf new file mode 100644 index 0000000000..61763734db --- /dev/null +++ b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationHandler.inf @@ -0,0 +1,96 @@ +## @file +# Provides security service of image verification +# +# This library hooks LoadImage() API to verify every image by the verification policy. +# +# Caution: This module requires additional review when modified. +# This library will have external input - PE/COFF image. +# This external input must be validated carefully to avoid security issues such as +# buffer overflow or integer overflow. +# +# Copyright (c) 2009 - 2018, Intel Corporation. All rights reserved.
+# SPDX-License-Identifier: BSD-2-Clause-Patent +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = DxeImageVerificationHandler + MODULE_UNI_FILE = DxeImageVerificationLib.uni + FILE_GUID = 0CA970E1-43FA-4402-BC0A-81AF336BFFD6 + MODULE_TYPE = DXE_DRIVER + VERSION_STRING = 1.0 + LIBRARY_CLASS = NULL|DXE_DRIVER DXE_RUNTIME_DRIVER DXE_SMM_DRIVER UEFI_APPLICATION UEFI_DRIVER + CONSTRUCTOR = DxeImageVerificationLibConstructor + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = IA32 X64 EBC +# + +[Sources] + DxeImageVerificationLib.c + DxeImageVerificationHandler.c + ImageVerificationLibInternal.h + Measurement.c + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + CryptoPkg/CryptoPkg.dec + SecurityPkg/SecurityPkg.dec + +[LibraryClasses] + MemoryAllocationLib + BaseLib + UefiLib + UefiBootServicesTableLib + UefiRuntimeServicesTableLib + BaseMemoryLib + DebugLib + DevicePathLib + BaseCryptLib + SecurityManagementLib + PeCoffLib + TpmMeasurementLib + +[Protocols] + gEfiFirmwareVolume2ProtocolGuid ## SOMETIMES_CONSUMES + gEfiBlockIoProtocolGuid ## SOMETIMES_CONSUMES + gEfiSimpleFileSystemProtocolGuid ## SOMETIMES_CONSUMES + +[Guids] + ## SOMETIMES_CONSUMES ## Variable:L"DB" + ## SOMETIMES_CONSUMES ## Variable:L"DBX" + ## SOMETIMES_CONSUMES ## Variable:L"DBT" + ## PRODUCES ## SystemTable + ## CONSUMES ## SystemTable + gEfiImageSecurityDatabaseGuid + + ## SOMETIMES_CONSUMES ## GUID # Unique ID for the type of the signature. + ## SOMETIMES_PRODUCES ## GUID # Unique ID for the type of the signature. + gEfiCertSha1Guid + + ## SOMETIMES_CONSUMES ## GUID # Unique ID for the type of the signature. + ## SOMETIMES_PRODUCES ## GUID # Unique ID for the type of the signature. + gEfiCertSha256Guid + + ## SOMETIMES_CONSUMES ## GUID # Unique ID for the type of the signature. + ## SOMETIMES_PRODUCES ## GUID # Unique ID for the type of the signature. + gEfiCertSha384Guid + + ## SOMETIMES_CONSUMES ## GUID # Unique ID for the type of the signature. + ## SOMETIMES_PRODUCES ## GUID # Unique ID for the type of the signature. + gEfiCertSha512Guid + + gEfiCertX509Guid ## SOMETIMES_CONSUMES ## GUID # Unique ID for the type of the signature. + gEfiCertX509Sha256Guid ## SOMETIMES_CONSUMES ## GUID # Unique ID for the type of the signature. + gEfiCertX509Sha384Guid ## SOMETIMES_CONSUMES ## GUID # Unique ID for the type of the signature. + gEfiCertX509Sha512Guid ## SOMETIMES_CONSUMES ## GUID # Unique ID for the type of the signature. + gEfiCertPkcs7Guid ## SOMETIMES_CONSUMES ## GUID # Unique ID for the type of the certificate. + +[Pcd] + gEfiSecurityPkgTokenSpaceGuid.PcdOptionRomImageVerificationPolicy ## SOMETIMES_CONSUMES + gEfiSecurityPkgTokenSpaceGuid.PcdRemovableMediaImageVerificationPolicy ## SOMETIMES_CONSUMES + gEfiSecurityPkgTokenSpaceGuid.PcdFixedMediaImageVerificationPolicy ## SOMETIMES_CONSUMES diff --git a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.c b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.c index b05da19c2b..903020ea80 100644 --- a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.c +++ b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.c @@ -1,5 +1,5 @@ /** @file - Implement image verification services for secure boot service + Implement image verification services. Caution: This file requires additional review when modified. This library will have external input - PE/COFF image. @@ -18,33 +18,12 @@ SPDX-License-Identifier: BSD-2-Clause-Patent **/ -#include "DxeImageVerificationLib.h" - -// -// Caution: This is used by a function which may receive untrusted input. -// These global variables hold PE/COFF image data, and they should be validated before use. -// -EFI_IMAGE_OPTIONAL_HEADER_PTR_UNION mNtHeader; -UINT32 mPeCoffHeaderOffset; -EFI_GUID mCertType; - +#include "ImageVerificationLibInternal.h" // // Information on current PE/COFF image // -UINTN mImageSize; -UINT8 *mImageBase = NULL; -UINT8 mImageDigest[MAX_DIGEST_SIZE]; -UINTN mImageDigestSize; - -// -// Notify string for authorization UI. -// -CHAR16 mNotifyString1[MAX_NOTIFY_STRING_LEN] = L"Image verification pass but not found in authorized database!"; -CHAR16 mNotifyString2[MAX_NOTIFY_STRING_LEN] = L"Launch this image anyway? (Yes/Defer/No)"; -// -// Public Exponent of RSA Key. -// -CONST UINT8 mRsaE[] = { 0x01, 0x00, 0x01 }; +STATIC UINTN LocalImageSize; +STATIC UINT8 *LocalImageBase = NULL; // // OID ASN.1 Value for Hash Algorithms @@ -69,7 +48,25 @@ HASH_TABLE mHash[] = { { L"SHA512", 64, &mHashOidValue[32], 9, Sha512GetContextSize, Sha512Init, Sha512Update, Sha512Final } }; -EFI_STRING mHashTypeStr; +/** + Get the length of the digest of given hash algorithm. + + @param[in] HashAlg Hash algorithm type. + + @retval UINTN Length of the digest of given hash algorithm. + +**/ +UINTN +GetDigestLength ( + IN UINT32 HashAlg + ) +{ + if (HashAlg >= HASHALG_MAX) { + return 0; + } + + return mHash[HashAlg].DigestLength; +} /** SecureBoot Hook for processing image verification. @@ -125,11 +122,11 @@ DxeImageVerificationLibImageRead ( } EndPosition = FileOffset + *ReadSize; - if (EndPosition > mImageSize) { - *ReadSize = (UINT32)(mImageSize - FileOffset); + if (EndPosition > LocalImageSize) { + *ReadSize = (UINT32)(LocalImageSize - FileOffset); } - if (FileOffset >= mImageSize) { + if (FileOffset >= LocalImageSize) { *ReadSize = 0; } @@ -138,137 +135,90 @@ DxeImageVerificationLibImageRead ( return EFI_SUCCESS; } -/** - Get the image type. - - @param[in] File This is a pointer to the device path of the file that is - being dispatched. - - @return UINT32 Image Type -**/ -UINT32 -GetImageType ( - IN CONST EFI_DEVICE_PATH_PROTOCOL *File +EFI_STATUS +GetImagePeCoffOffset ( + IN VOID *FileBase, + IN UINTN FileSize, + OUT UINT32 *PeCoffHeaderOffset ) { - EFI_STATUS Status; - EFI_HANDLE DeviceHandle; - EFI_DEVICE_PATH_PROTOCOL *TempDevicePath; - EFI_BLOCK_IO_PROTOCOL *BlockIo; - - if (File == NULL) { - return IMAGE_UNKNOWN; - } - - // - // First check to see if File is from a Firmware Volume - // - DeviceHandle = NULL; - TempDevicePath = (EFI_DEVICE_PATH_PROTOCOL *)File; - Status = gBS->LocateDevicePath ( - &gEfiFirmwareVolume2ProtocolGuid, - &TempDevicePath, - &DeviceHandle - ); - if (!EFI_ERROR (Status)) { - Status = gBS->OpenProtocol ( - DeviceHandle, - &gEfiFirmwareVolume2ProtocolGuid, - NULL, - NULL, - NULL, - EFI_OPEN_PROTOCOL_TEST_PROTOCOL - ); - if (!EFI_ERROR (Status)) { - return IMAGE_FROM_FV; - } - } + EFI_IMAGE_DOS_HEADER *DosHdr; + PE_COFF_LOADER_IMAGE_CONTEXT ImageContext; + RETURN_STATUS PeCoffStatus; - // - // Next check to see if File is from a Block I/O device - // - DeviceHandle = NULL; - TempDevicePath = (EFI_DEVICE_PATH_PROTOCOL *)File; - Status = gBS->LocateDevicePath ( - &gEfiBlockIoProtocolGuid, - &TempDevicePath, - &DeviceHandle - ); - if (!EFI_ERROR (Status)) { - BlockIo = NULL; - Status = gBS->OpenProtocol ( - DeviceHandle, - &gEfiBlockIoProtocolGuid, - (VOID **)&BlockIo, - NULL, - NULL, - EFI_OPEN_PROTOCOL_GET_PROTOCOL - ); - if (!EFI_ERROR (Status) && (BlockIo != NULL)) { - if (BlockIo->Media != NULL) { - if (BlockIo->Media->RemovableMedia) { - // - // Block I/O is present and specifies the media is removable - // - return IMAGE_FROM_REMOVABLE_MEDIA; - } else { - // - // Block I/O is present and specifies the media is not removable - // - return IMAGE_FROM_FIXED_MEDIA; - } - } - } + if (PeCoffHeaderOffset == NULL || FileBase == NULL || FileSize == 0) { + return EFI_INVALID_PARAMETER; } + LocalImageSize = FileSize; + LocalImageBase = FileBase; + + ZeroMem (&ImageContext, sizeof (ImageContext)); + ImageContext.Handle = (VOID *)FileBase; + ImageContext.ImageRead = (PE_COFF_LOADER_READ_FILE)DxeImageVerificationLibImageRead; + // - // File is not in a Firmware Volume or on a Block I/O device, so check to see if - // the device path supports the Simple File System Protocol. + // Get information about the image being loaded // - DeviceHandle = NULL; - TempDevicePath = (EFI_DEVICE_PATH_PROTOCOL *)File; - Status = gBS->LocateDevicePath ( - &gEfiSimpleFileSystemProtocolGuid, - &TempDevicePath, - &DeviceHandle - ); - if (!EFI_ERROR (Status)) { + PeCoffStatus = PeCoffLoaderGetImageInfo (&ImageContext); + if (RETURN_ERROR (PeCoffStatus)) { + LocalImageBase = NULL; + LocalImageSize = 0; + // - // Simple File System is present without Block I/O, so assume media is fixed. + // The information can't be got from the invalid PeImage // - return IMAGE_FROM_FIXED_MEDIA; + DEBUG ((DEBUG_INFO, "DxeImageVerificationLib: PeImage invalid. Cannot retrieve image information.\n")); + return EFI_UNSUPPORTED; } - // - // File is not from an FV, Block I/O or Simple File System, so the only options - // left are a PCI Option ROM and a Load File Protocol such as a PXE Boot from a NIC. - // - TempDevicePath = (EFI_DEVICE_PATH_PROTOCOL *)File; - while (!IsDevicePathEndType (TempDevicePath)) { - switch (DevicePathType (TempDevicePath)) { - case MEDIA_DEVICE_PATH: - if (DevicePathSubType (TempDevicePath) == MEDIA_RELATIVE_OFFSET_RANGE_DP) { - return IMAGE_FROM_OPTION_ROM; - } + LocalImageBase = NULL; + LocalImageSize = 0; - break; + DosHdr = (EFI_IMAGE_DOS_HEADER *)FileBase; + if (DosHdr->e_magic == EFI_IMAGE_DOS_SIGNATURE) { + // + // DOS image header is present, + // so read the PE header after the DOS image header. + // + *PeCoffHeaderOffset = DosHdr->e_lfanew; + } else { + *PeCoffHeaderOffset = 0; + } - case MESSAGING_DEVICE_PATH: - if (DevicePathSubType (TempDevicePath) == MSG_MAC_ADDR_DP) { - return IMAGE_FROM_REMOVABLE_MEDIA; - } + return EFI_SUCCESS; +} - break; +EFI_IMAGE_OPTIONAL_HEADER_UNION * +GetImageNtHeader ( + IN VOID *FileBase, + IN UINTN FileSize + ) +{ + EFI_STATUS Status; + UINT32 PeCoffHeaderOffset; + EFI_IMAGE_OPTIONAL_HEADER_UNION *NtHeader; - default: - break; - } + Status = GetImagePeCoffOffset (FileBase, FileSize, &PeCoffHeaderOffset); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_INFO, "PE Coff offset not found\n")); + return NULL; + } - TempDevicePath = NextDevicePathNode (TempDevicePath); + NtHeader = (EFI_IMAGE_OPTIONAL_HEADER_UNION *)(FileBase + PeCoffHeaderOffset); + // + // Check PE/COFF image. + // + if (NtHeader->Pe32.Signature != EFI_IMAGE_NT_SIGNATURE) { + // + // It is not a valid Pe/Coff file. + // + DEBUG ((DEBUG_INFO, "DxeImageVerificationLib: Not a valid PE/COFF image.\n")); + return NULL; } - return IMAGE_UNKNOWN; + return NtHeader; } /** @@ -279,10 +229,12 @@ GetImageType ( PE/COFF image is external input, so this function will validate its data structure within this image buffer before use. - Notes: PE/COFF image has been checked by BasePeCoffLib PeCoffLoaderGetImageInfo() in - its caller function DxeImageVerificationHandler(). - - @param[in] HashAlg Hash algorithm type. + @param[in] ImageBase Address to the file content. + @param[in] ImageSize File size. + @param[in] HashAlg Hash algorithm type. + @param[out] ImageDigest Buffer containing digest of the file. + @param[out] ImageDigestSize Size of the digest buffer. + @param[out] CertType GUID representing the hash type @retval TRUE Successfully hash image. @retval FALSE Fail in hash image. @@ -290,21 +242,27 @@ GetImageType ( **/ BOOLEAN HashPeImage ( - IN UINT32 HashAlg + IN VOID* ImageBase, + IN UINTN ImageSize, + IN UINT32 HashAlg, + OUT UINT8 *ImageDigest, + OUT UINTN *ImageDigestSize, + OUT EFI_GUID *CertType ) { - BOOLEAN Status; - EFI_IMAGE_SECTION_HEADER *Section; - VOID *HashCtx; - UINTN CtxSize; - UINT8 *HashBase; - UINTN HashSize; - UINTN SumOfBytesHashed; - EFI_IMAGE_SECTION_HEADER *SectionHeader; - UINTN Index; - UINTN Pos; - UINT32 CertSize; - UINT32 NumberOfRvaAndSizes; + BOOLEAN Status; + EFI_IMAGE_SECTION_HEADER *Section; + VOID *HashCtx; + UINTN CtxSize; + UINT8 *HashBase; + UINTN HashSize; + UINTN SumOfBytesHashed; + EFI_IMAGE_SECTION_HEADER *SectionHeader; + UINTN Index; + UINTN Pos; + UINT32 CertSize; + UINT32 NumberOfRvaAndSizes; + EFI_IMAGE_OPTIONAL_HEADER_UNION *NtHeader; HashCtx = NULL; SectionHeader = NULL; @@ -314,39 +272,50 @@ HashPeImage ( return FALSE; } + if ((ImageDigest == NULL) || (ImageDigestSize == NULL) || (CertType == NULL)) { + return FALSE; + } + + + if ((mHash[HashAlg].GetContextSize == NULL) || + (mHash[HashAlg].HashInit == NULL) || + (mHash[HashAlg].HashUpdate == NULL) || + (mHash[HashAlg].HashFinal == NULL)) { + return FALSE; + } + // // Initialize context of hash. // - ZeroMem (mImageDigest, MAX_DIGEST_SIZE); + ZeroMem (ImageDigest, MAX_DIGEST_SIZE); switch (HashAlg) { #ifndef DISABLE_SHA1_DEPRECATED_INTERFACES case HASHALG_SHA1: - mImageDigestSize = SHA1_DIGEST_SIZE; - mCertType = gEfiCertSha1Guid; + *ImageDigestSize = SHA1_DIGEST_SIZE; + CopyGuid (CertType, &gEfiCertSha1Guid); break; #endif case HASHALG_SHA256: - mImageDigestSize = SHA256_DIGEST_SIZE; - mCertType = gEfiCertSha256Guid; + *ImageDigestSize = SHA256_DIGEST_SIZE; + CopyGuid (CertType, &gEfiCertSha256Guid); break; case HASHALG_SHA384: - mImageDigestSize = SHA384_DIGEST_SIZE; - mCertType = gEfiCertSha384Guid; + *ImageDigestSize = SHA384_DIGEST_SIZE; + CopyGuid (CertType, &gEfiCertSha384Guid); break; case HASHALG_SHA512: - mImageDigestSize = SHA512_DIGEST_SIZE; - mCertType = gEfiCertSha512Guid; + *ImageDigestSize = SHA512_DIGEST_SIZE; + CopyGuid (CertType, &gEfiCertSha512Guid); break; default: return FALSE; } - mHashTypeStr = mHash[HashAlg].Name; CtxSize = mHash[HashAlg].GetContextSize (); HashCtx = AllocatePool (CtxSize); @@ -363,6 +332,10 @@ HashPeImage ( goto Done; } + NtHeader = GetImageNtHeader (ImageBase, ImageSize); + if (NtHeader == NULL) { + goto Done; + } // // Measuring PE/COFF Image Header; // But CheckSum field and SECURITY data directory (certificate) are excluded @@ -372,19 +345,19 @@ HashPeImage ( // 3. Calculate the distance from the base of the image header to the image checksum address. // 4. Hash the image header from its base to beginning of the image checksum. // - HashBase = mImageBase; - if (mNtHeader.Pe32->OptionalHeader.Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) { + HashBase = ImageBase; + if (NtHeader->Pe32.OptionalHeader.Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) { // // Use PE32 offset. // - HashSize = (UINTN)(&mNtHeader.Pe32->OptionalHeader.CheckSum) - (UINTN)HashBase; - NumberOfRvaAndSizes = mNtHeader.Pe32->OptionalHeader.NumberOfRvaAndSizes; - } else if (mNtHeader.Pe32->OptionalHeader.Magic == EFI_IMAGE_NT_OPTIONAL_HDR64_MAGIC) { + HashSize = (UINTN)(&NtHeader->Pe32.OptionalHeader.CheckSum) - (UINTN)HashBase; + NumberOfRvaAndSizes = NtHeader->Pe32.OptionalHeader.NumberOfRvaAndSizes; + } else if (NtHeader->Pe32.OptionalHeader.Magic == EFI_IMAGE_NT_OPTIONAL_HDR64_MAGIC) { // // Use PE32+ offset. // - HashSize = (UINTN)(&mNtHeader.Pe32Plus->OptionalHeader.CheckSum) - (UINTN)HashBase; - NumberOfRvaAndSizes = mNtHeader.Pe32Plus->OptionalHeader.NumberOfRvaAndSizes; + HashSize = (UINTN)(&NtHeader->Pe32Plus.OptionalHeader.CheckSum) - (UINTN)HashBase; + NumberOfRvaAndSizes = NtHeader->Pe32Plus.OptionalHeader.NumberOfRvaAndSizes; } else { // // Invalid header magic number. @@ -406,18 +379,18 @@ HashPeImage ( // 6. Since there is no Cert Directory in optional header, hash everything // from the end of the checksum to the end of image header. // - if (mNtHeader.Pe32->OptionalHeader.Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) { + if (NtHeader->Pe32.OptionalHeader.Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) { // // Use PE32 offset. // - HashBase = (UINT8 *)&mNtHeader.Pe32->OptionalHeader.CheckSum + sizeof (UINT32); - HashSize = mNtHeader.Pe32->OptionalHeader.SizeOfHeaders - ((UINTN)HashBase - (UINTN)mImageBase); + HashBase = (UINT8 *)&NtHeader->Pe32.OptionalHeader.CheckSum + sizeof (UINT32); + HashSize = NtHeader->Pe32.OptionalHeader.SizeOfHeaders - ((UINTN)HashBase - (UINTN)ImageBase); } else { // // Use PE32+ offset. // - HashBase = (UINT8 *)&mNtHeader.Pe32Plus->OptionalHeader.CheckSum + sizeof (UINT32); - HashSize = mNtHeader.Pe32Plus->OptionalHeader.SizeOfHeaders - ((UINTN)HashBase - (UINTN)mImageBase); + HashBase = (UINT8 *)&NtHeader->Pe32Plus.OptionalHeader.CheckSum + sizeof (UINT32); + HashSize = NtHeader->Pe32Plus.OptionalHeader.SizeOfHeaders - ((UINTN)HashBase - (UINTN)ImageBase); } if (HashSize != 0) { @@ -430,18 +403,18 @@ HashPeImage ( // // 7. Hash everything from the end of the checksum to the start of the Cert Directory. // - if (mNtHeader.Pe32->OptionalHeader.Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) { + if (NtHeader->Pe32.OptionalHeader.Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) { // // Use PE32 offset. // - HashBase = (UINT8 *)&mNtHeader.Pe32->OptionalHeader.CheckSum + sizeof (UINT32); - HashSize = (UINTN)(&mNtHeader.Pe32->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY]) - (UINTN)HashBase; + HashBase = (UINT8 *)&NtHeader->Pe32.OptionalHeader.CheckSum + sizeof (UINT32); + HashSize = (UINTN)(&NtHeader->Pe32.OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY]) - (UINTN)HashBase; } else { // // Use PE32+ offset. // - HashBase = (UINT8 *)&mNtHeader.Pe32Plus->OptionalHeader.CheckSum + sizeof (UINT32); - HashSize = (UINTN)(&mNtHeader.Pe32Plus->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY]) - (UINTN)HashBase; + HashBase = (UINT8 *)&NtHeader->Pe32Plus.OptionalHeader.CheckSum + sizeof (UINT32); + HashSize = (UINTN)(&NtHeader->Pe32Plus.OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY]) - (UINTN)HashBase; } if (HashSize != 0) { @@ -455,18 +428,18 @@ HashPeImage ( // 8. Skip over the Cert Directory. (It is sizeof(IMAGE_DATA_DIRECTORY) bytes.) // 9. Hash everything from the end of the Cert Directory to the end of image header. // - if (mNtHeader.Pe32->OptionalHeader.Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) { + if (NtHeader->Pe32.OptionalHeader.Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) { // // Use PE32 offset // - HashBase = (UINT8 *)&mNtHeader.Pe32->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY + 1]; - HashSize = mNtHeader.Pe32->OptionalHeader.SizeOfHeaders - ((UINTN)HashBase - (UINTN)mImageBase); + HashBase = (UINT8 *)&NtHeader->Pe32.OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY + 1]; + HashSize = NtHeader->Pe32.OptionalHeader.SizeOfHeaders - ((UINTN)HashBase - (UINTN)ImageBase); } else { // // Use PE32+ offset. // - HashBase = (UINT8 *)&mNtHeader.Pe32Plus->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY + 1]; - HashSize = mNtHeader.Pe32Plus->OptionalHeader.SizeOfHeaders - ((UINTN)HashBase - (UINTN)mImageBase); + HashBase = (UINT8 *)&NtHeader->Pe32Plus.OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY + 1]; + HashSize = NtHeader->Pe32Plus.OptionalHeader.SizeOfHeaders - ((UINTN)HashBase - (UINTN)ImageBase); } if (HashSize != 0) { @@ -480,24 +453,23 @@ HashPeImage ( // // 10. Set the SUM_OF_BYTES_HASHED to the size of the header. // - if (mNtHeader.Pe32->OptionalHeader.Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) { + if (NtHeader->Pe32.OptionalHeader.Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) { // // Use PE32 offset. // - SumOfBytesHashed = mNtHeader.Pe32->OptionalHeader.SizeOfHeaders; + SumOfBytesHashed = NtHeader->Pe32.OptionalHeader.SizeOfHeaders; } else { // // Use PE32+ offset // - SumOfBytesHashed = mNtHeader.Pe32Plus->OptionalHeader.SizeOfHeaders; + SumOfBytesHashed = NtHeader->Pe32Plus.OptionalHeader.SizeOfHeaders; } Section = (EFI_IMAGE_SECTION_HEADER *)( - mImageBase + - mPeCoffHeaderOffset + + (UINTN)(NtHeader) + sizeof (UINT32) + sizeof (EFI_IMAGE_FILE_HEADER) + - mNtHeader.Pe32->FileHeader.SizeOfOptionalHeader + NtHeader->Pe32.FileHeader.SizeOfOptionalHeader ); // @@ -506,7 +478,7 @@ HashPeImage ( // header indicates how big the table should be. Do not include any // IMAGE_SECTION_HEADERs in the table whose 'SizeOfRawData' field is zero. // - SectionHeader = (EFI_IMAGE_SECTION_HEADER *)AllocateZeroPool (sizeof (EFI_IMAGE_SECTION_HEADER) * mNtHeader.Pe32->FileHeader.NumberOfSections); + SectionHeader = (EFI_IMAGE_SECTION_HEADER *)AllocateZeroPool (sizeof (EFI_IMAGE_SECTION_HEADER) * NtHeader->Pe32.FileHeader.NumberOfSections); if (SectionHeader == NULL) { Status = FALSE; goto Done; @@ -518,7 +490,7 @@ HashPeImage ( // words, sort the section headers according to the disk-file offset of // the section. // - for (Index = 0; Index < mNtHeader.Pe32->FileHeader.NumberOfSections; Index++) { + for (Index = 0; Index < NtHeader->Pe32.FileHeader.NumberOfSections; Index++) { Pos = Index; while ((Pos > 0) && (Section->PointerToRawData < SectionHeader[Pos - 1].PointerToRawData)) { CopyMem (&SectionHeader[Pos], &SectionHeader[Pos - 1], sizeof (EFI_IMAGE_SECTION_HEADER)); @@ -536,13 +508,13 @@ HashPeImage ( // 14. Add the section's 'SizeOfRawData' to SUM_OF_BYTES_HASHED . // 15. Repeat steps 13 and 14 for all the sections in the sorted table. // - for (Index = 0; Index < mNtHeader.Pe32->FileHeader.NumberOfSections; Index++) { + for (Index = 0; Index < NtHeader->Pe32.FileHeader.NumberOfSections; Index++) { Section = &SectionHeader[Index]; if (Section->SizeOfRawData == 0) { continue; } - HashBase = mImageBase + Section->PointerToRawData; + HashBase = ImageBase + Section->PointerToRawData; HashSize = (UINTN)Section->SizeOfRawData; Status = mHash[HashAlg].HashUpdate (HashCtx, HashBase, HashSize); @@ -559,39 +531,39 @@ HashPeImage ( // at file offset SUM_OF_BYTES_HASHED and its length is: // FileSize - (CertDirectory->Size) // - if (mImageSize > SumOfBytesHashed) { - HashBase = mImageBase + SumOfBytesHashed; + if (ImageSize > SumOfBytesHashed) { + HashBase = ImageBase + SumOfBytesHashed; if (NumberOfRvaAndSizes <= EFI_IMAGE_DIRECTORY_ENTRY_SECURITY) { CertSize = 0; } else { - if (mNtHeader.Pe32->OptionalHeader.Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) { + if (NtHeader->Pe32.OptionalHeader.Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) { // // Use PE32 offset. // - CertSize = mNtHeader.Pe32->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY].Size; + CertSize = NtHeader->Pe32.OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY].Size; } else { // // Use PE32+ offset. // - CertSize = mNtHeader.Pe32Plus->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY].Size; + CertSize = NtHeader->Pe32Plus.OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY].Size; } } - if (mImageSize > CertSize + SumOfBytesHashed) { - HashSize = (UINTN)(mImageSize - CertSize - SumOfBytesHashed); + if (ImageSize > CertSize + SumOfBytesHashed) { + HashSize = (UINTN)(ImageSize - CertSize - SumOfBytesHashed); Status = mHash[HashAlg].HashUpdate (HashCtx, HashBase, HashSize); if (!Status) { goto Done; } - } else if (mImageSize < CertSize + SumOfBytesHashed) { + } else if (ImageSize < CertSize + SumOfBytesHashed) { Status = FALSE; goto Done; } } - Status = mHash[HashAlg].HashFinal (HashCtx, mImageDigest); + Status = mHash[HashAlg].HashFinal (HashCtx, ImageDigest); Done: if (HashCtx != NULL) { @@ -614,8 +586,13 @@ HashPeImage ( PE/COFF image is external input, so this function will validate its data structure within this image buffer before use. + @param[in] ImageBase Pointer to the file content. + @param[in] ImageSize Size of the file. @param[in] AuthData Pointer to the Authenticode Signature retrieved from signed image. @param[in] AuthDataSize Size of the Authenticode Signature in bytes. + @param[out] ImageDigest Buffer with the file digest. + @param[out] ImageDigestSize Size of the file digest. + @param[out] CertType GUID representing the type of hash. @retval EFI_UNSUPPORTED Hash algorithm is not supported. @retval EFI_SUCCESS Hash successfully. @@ -623,12 +600,24 @@ HashPeImage ( **/ EFI_STATUS HashPeImageByType ( - IN UINT8 *AuthData, - IN UINTN AuthDataSize + IN VOID *ImageBase, + IN UINTN ImageSize, + IN UINT8 *AuthData, + IN UINTN AuthDataSize, + OUT UINT8 *ImageDigest, + OUT UINTN *ImageDigestSize, + OUT EFI_GUID *CertType ) { UINT8 Index; + if ((ImageBase == NULL) || (ImageSize == 0) || + (AuthData == NULL) || (AuthDataSize == 0) || + (ImageDigest == NULL) || (ImageDigestSize == NULL) || + (CertType == NULL)) { + return EFI_INVALID_PARAMETER; + } + for (Index = 0; Index < HASHALG_MAX; Index++) { // // Check the Hash algorithm in PE/COFF Authenticode. @@ -665,7 +654,7 @@ HashPeImageByType ( // // HASH PE Image based on Hash algorithm in PE/COFF Authenticode. // - if (!HashPeImage (Index)) { + if (!HashPeImage (ImageBase, ImageSize, Index, ImageDigest, ImageDigestSize, CertType)) { return EFI_UNSUPPORTED; } @@ -673,159 +662,99 @@ HashPeImageByType ( } /** - Returns the size of a given image execution info table in bytes. - - This function returns the size, in bytes, of the image execution info table specified by - ImageExeInfoTable. If ImageExeInfoTable is NULL, then 0 is returned. + Calculate the hash of a certificate data with the specified hash algorithm. - @param ImageExeInfoTable A pointer to a image execution info table structure. + @param[in] CertData The certificate data to be hashed. + @param[in] CertSize The certificate size in bytes. + @param[in] HashAlg The specified hash algorithm. + @param[out] CertHash The output digest of the certificate - @retval 0 If ImageExeInfoTable is NULL. - @retval Others The size of a image execution info table in bytes. + @retval TRUE Successfully got the hash of the CertData. + @retval FALSE Failed to get the hash of CertData. **/ -UINTN -GetImageExeInfoTableSize ( - EFI_IMAGE_EXECUTION_INFO_TABLE *ImageExeInfoTable +BOOLEAN +CalculateCertHash ( + IN UINT8 *CertData, + IN UINTN CertSize, + IN UINT32 HashAlg, + OUT UINT8 *CertHash ) { - UINTN Index; - EFI_IMAGE_EXECUTION_INFO *ImageExeInfoItem; - UINTN TotalSize; - - if (ImageExeInfoTable == NULL) { - return 0; - } - - ImageExeInfoItem = (EFI_IMAGE_EXECUTION_INFO *)((UINT8 *)ImageExeInfoTable + sizeof (EFI_IMAGE_EXECUTION_INFO_TABLE)); - TotalSize = sizeof (EFI_IMAGE_EXECUTION_INFO_TABLE); - for (Index = 0; Index < ImageExeInfoTable->NumberOfImages; Index++) { - TotalSize += ReadUnaligned32 ((UINT32 *)&ImageExeInfoItem->InfoSize); - ImageExeInfoItem = (EFI_IMAGE_EXECUTION_INFO *)((UINT8 *)ImageExeInfoItem + ReadUnaligned32 ((UINT32 *)&ImageExeInfoItem->InfoSize)); - } + BOOLEAN Status; + VOID *HashCtx; + UINTN CtxSize; + UINT8 *TBSCert; + UINTN TBSCertSize; - return TotalSize; -} - -/** - Create an Image Execution Information Table entry and add it to system configuration table. + HashCtx = NULL; + Status = FALSE; - @param[in] Action Describes the action taken by the firmware regarding this image. - @param[in] Name Input a null-terminated, user-friendly name. - @param[in] DevicePath Input device path pointer. - @param[in] Signature Input signature info in EFI_SIGNATURE_LIST data structure. - @param[in] SignatureSize Size of signature. Must be zero if Signature is NULL. - -**/ -VOID -AddImageExeInfo ( - IN EFI_IMAGE_EXECUTION_ACTION Action, - IN CHAR16 *Name OPTIONAL, - IN CONST EFI_DEVICE_PATH_PROTOCOL *DevicePath, - IN EFI_SIGNATURE_LIST *Signature OPTIONAL, - IN UINTN SignatureSize - ) -{ - EFI_IMAGE_EXECUTION_INFO_TABLE *ImageExeInfoTable; - EFI_IMAGE_EXECUTION_INFO_TABLE *NewImageExeInfoTable; - EFI_IMAGE_EXECUTION_INFO *ImageExeInfoEntry; - UINTN ImageExeInfoTableSize; - UINTN NewImageExeInfoEntrySize; - UINTN NameStringLen; - UINTN DevicePathSize; - CHAR16 *NameStr; - - ImageExeInfoTable = NULL; - NewImageExeInfoTable = NULL; - ImageExeInfoEntry = NULL; - NameStringLen = 0; - NameStr = NULL; - - if (DevicePath == NULL) { - return; - } - - if (Name != NULL) { - NameStringLen = StrSize (Name); - } else { - NameStringLen = sizeof (CHAR16); + if (HashAlg >= HASHALG_MAX) { + return FALSE; } - EfiGetSystemConfigurationTable (&gEfiImageSecurityDatabaseGuid, (VOID **)&ImageExeInfoTable); - if (ImageExeInfoTable != NULL) { - // - // The table has been found! - // We must enlarge the table to accommodate the new exe info entry. - // - ImageExeInfoTableSize = GetImageExeInfoTableSize (ImageExeInfoTable); - } else { - // - // Not Found! - // We should create a new table to append to the configuration table. - // - ImageExeInfoTableSize = sizeof (EFI_IMAGE_EXECUTION_INFO_TABLE); + if (CertHash == NULL || CertData == NULL || CertSize == 0) { + return FALSE; } - DevicePathSize = GetDevicePathSize (DevicePath); - // - // Signature size can be odd. Pad after signature to ensure next EXECUTION_INFO entry align + // Calculate the hash value of current TBSCertificate for comparision. // - ASSERT (Signature != NULL || SignatureSize == 0); - NewImageExeInfoEntrySize = sizeof (EFI_IMAGE_EXECUTION_INFO) + NameStringLen + DevicePathSize + SignatureSize; - - NewImageExeInfoTable = (EFI_IMAGE_EXECUTION_INFO_TABLE *)AllocateRuntimePool (ImageExeInfoTableSize + NewImageExeInfoEntrySize); - if (NewImageExeInfoTable == NULL) { - return; + if ((mHash[HashAlg].GetContextSize == NULL) || + (mHash[HashAlg].HashInit == NULL) || + (mHash[HashAlg].HashUpdate == NULL) || + (mHash[HashAlg].HashFinal == NULL)) { + return FALSE; } - if (ImageExeInfoTable != NULL) { - CopyMem (NewImageExeInfoTable, ImageExeInfoTable, ImageExeInfoTableSize); - } else { - NewImageExeInfoTable->NumberOfImages = 0; + // + // Retrieve the TBSCertificate for Hash Calculation. + // + if (!X509GetTBSCert (CertData, CertSize, &TBSCert, &TBSCertSize)) { + return FALSE; } - NewImageExeInfoTable->NumberOfImages++; - ImageExeInfoEntry = (EFI_IMAGE_EXECUTION_INFO *)((UINT8 *)NewImageExeInfoTable + ImageExeInfoTableSize); // - // Update new item's information. + // 1. Initialize context of hash. // - WriteUnaligned32 ((UINT32 *)ImageExeInfoEntry, Action); - WriteUnaligned32 ((UINT32 *)((UINT8 *)ImageExeInfoEntry + sizeof (EFI_IMAGE_EXECUTION_ACTION)), (UINT32)NewImageExeInfoEntrySize); - - NameStr = (CHAR16 *)(ImageExeInfoEntry + 1); - if (Name != NULL) { - CopyMem ((UINT8 *)NameStr, Name, NameStringLen); - } else { - ZeroMem ((UINT8 *)NameStr, sizeof (CHAR16)); + CtxSize = mHash[HashAlg].GetContextSize (); + HashCtx = AllocatePool (CtxSize); + if (HashCtx == NULL) { + return FALSE; } - CopyMem ( - (UINT8 *)NameStr + NameStringLen, - DevicePath, - DevicePathSize - ); - if (Signature != NULL) { - CopyMem ( - (UINT8 *)NameStr + NameStringLen + DevicePathSize, - Signature, - SignatureSize - ); + // + // 2. Initialize a hash context. + // + Status = mHash[HashAlg].HashInit (HashCtx); + if (!Status) { + goto Done; } // - // Update/replace the image execution table. + // 3. Calculate the hash. // - gBS->InstallConfigurationTable (&gEfiImageSecurityDatabaseGuid, (VOID *)NewImageExeInfoTable); + Status = mHash[HashAlg].HashUpdate (HashCtx, TBSCert, TBSCertSize); + if (!Status) { + goto Done; + } // - // Free Old table data! + // 4. Get the hash result. // - if (ImageExeInfoTable != NULL) { - FreePool (ImageExeInfoTable); + ZeroMem (CertHash, mHash[HashAlg].DigestLength); + Status = mHash[HashAlg].HashFinal (HashCtx, CertHash); + +Done: + if (HashCtx != NULL) { + FreePool (HashCtx); } + + return Status; } + /** Check whether the hash of an given X.509 certificate is in forbidden database (DBX). @@ -844,7 +773,7 @@ EFI_STATUS IsCertHashFoundInDbx ( IN UINT8 *Certificate, IN UINTN CertSize, - IN EFI_SIGNATURE_LIST *SignatureList, + IN EFI_SIGNATURE_LIST *SignatureList, OPTIONAL IN UINTN SignatureListSize, OUT EFI_TIME *RevocationTime, OUT BOOLEAN *IsFound @@ -857,29 +786,39 @@ IsCertHashFoundInDbx ( UINTN CertHashCount; UINTN Index; UINT32 HashAlg; - VOID *HashCtx; UINT8 CertDigest[MAX_DIGEST_SIZE]; UINT8 *DbxCertHash; UINTN SiglistHeaderSize; - UINT8 *TBSCert; - UINTN TBSCertSize; Status = EFI_ABORTED; *IsFound = FALSE; DbxList = SignatureList; DbxSize = SignatureListSize; - HashCtx = NULL; HashAlg = HASHALG_MAX; - if ((RevocationTime == NULL) || (DbxList == NULL)) { - return EFI_INVALID_PARAMETER; - } + if (DbxList == NULL) { + // + // Read signature database variable. + // + DbxSize = 0; + Status = gRT->GetVariable (EFI_IMAGE_SECURITY_DATABASE1, &gEfiImageSecurityDatabaseGuid, NULL, &DbxSize, NULL); + if (Status != EFI_BUFFER_TOO_SMALL) { + return EFI_SUCCESS; + } - // - // Retrieve the TBSCertificate from the X.509 Certificate. - // - if (!X509GetTBSCert (Certificate, CertSize, &TBSCert, &TBSCertSize)) { - return Status; + DbxList = (EFI_SIGNATURE_LIST *)AllocateZeroPool (DbxSize); + if (DbxList == NULL) { + return EFI_SUCCESS; + } + + Status = gRT->GetVariable (EFI_IMAGE_SECURITY_DATABASE1, &gEfiImageSecurityDatabaseGuid, NULL, &DbxSize, DbxList); + if (EFI_ERROR (Status)) { + goto Done; + } + + if (SignatureListSize == 0) { + SignatureListSize = DbxSize; + } } while ((DbxSize > 0) && (SignatureListSize >= DbxList->SignatureListSize)) { @@ -897,35 +836,13 @@ IsCertHashFoundInDbx ( DbxList = (EFI_SIGNATURE_LIST *)((UINT8 *)DbxList + DbxList->SignatureListSize); continue; } - // // Calculate the hash value of current TBSCertificate for comparision. // - if (mHash[HashAlg].GetContextSize == NULL) { - goto Done; - } - - ZeroMem (CertDigest, MAX_DIGEST_SIZE); - HashCtx = AllocatePool (mHash[HashAlg].GetContextSize ()); - if (HashCtx == NULL) { - goto Done; - } - - if (!mHash[HashAlg].HashInit (HashCtx)) { - goto Done; - } - - if (!mHash[HashAlg].HashUpdate (HashCtx, TBSCert, TBSCertSize)) { - goto Done; - } - - if (!mHash[HashAlg].HashFinal (HashCtx, CertDigest)) { + if (!CalculateCertHash (Certificate, CertSize, HashAlg, CertDigest)) { goto Done; } - FreePool (HashCtx); - HashCtx = NULL; - SiglistHeaderSize = sizeof (EFI_SIGNATURE_LIST) + DbxList->SignatureHeaderSize; CertHash = (EFI_SIGNATURE_DATA *)((UINT8 *)DbxList + SiglistHeaderSize); CertHashCount = (DbxList->SignatureListSize - SiglistHeaderSize) / DbxList->SignatureSize; @@ -941,10 +858,12 @@ IsCertHashFoundInDbx ( Status = EFI_SUCCESS; *IsFound = TRUE; - // - // Return the revocation time. - // - CopyMem (RevocationTime, (EFI_TIME *)(DbxCertHash + mHash[HashAlg].DigestLength), sizeof (EFI_TIME)); + if (RevocationTime != NULL) { + // + // Return the revocation time. + // + CopyMem (RevocationTime, (EFI_TIME *)(DbxCertHash + mHash[HashAlg].DigestLength), sizeof (EFI_TIME)); + } goto Done; } @@ -958,8 +877,8 @@ IsCertHashFoundInDbx ( Status = EFI_SUCCESS; Done: - if (HashCtx != NULL) { - FreePool (HashCtx); + if (SignatureList == NULL && DbxList != NULL) { + FreePool (DbxList); } return Status; @@ -1237,6 +1156,8 @@ PassTimestampCheck ( @param[in] AuthData Pointer to the Authenticode signature retrieved from the signed image. @param[in] AuthDataSize Size of the Authenticode signature in bytes. + @param[in] ImageDigest Buffer containing digest of the file. + @param[in] ImageDigestSize Size of the digest buffer. @retval TRUE Image is forbidden by dbx. @retval FALSE Image is not forbidden by dbx. @@ -1245,7 +1166,9 @@ PassTimestampCheck ( BOOLEAN IsForbiddenByDbx ( IN UINT8 *AuthData, - IN UINTN AuthDataSize + IN UINTN AuthDataSize, + IN UINT8 *ImageDigest, + IN UINTN ImageDigestSize ) { EFI_STATUS Status; @@ -1338,8 +1261,8 @@ IsForbiddenByDbx ( AuthDataSize, RootCert, RootCertSize, - mImageDigest, - mImageDigestSize + ImageDigest, + ImageDigestSize ); if (IsForbidden) { DEBUG ((DEBUG_INFO, "DxeImageVerificationLib: Image is signed but signature is forbidden by DBX.\n")); @@ -1431,8 +1354,10 @@ IsForbiddenByDbx ( /** Check whether the image signature can be verified by the trusted certificates in DB database. - @param[in] AuthData Pointer to the Authenticode signature retrieved from signed image. - @param[in] AuthDataSize Size of the Authenticode signature in bytes. + @param[in] AuthData Pointer to the Authenticode signature retrieved from signed image. + @param[in] AuthDataSize Size of the Authenticode signature in bytes. + @param[in] ImageDigest Buffer containing digest of the file. + @param[in] ImageDigestSize Size of the digest buffer. @retval TRUE Image passed verification using certificate in db. @retval FALSE Image didn't pass verification using certificate in db. @@ -1441,7 +1366,9 @@ IsForbiddenByDbx ( BOOLEAN IsAllowedByDb ( IN UINT8 *AuthData, - IN UINTN AuthDataSize + IN UINTN AuthDataSize, + IN UINT8 *ImageDigest, + IN UINTN ImageDigestSize ) { EFI_STATUS Status; @@ -1543,8 +1470,8 @@ IsAllowedByDb ( AuthDataSize, RootCert, RootCertSize, - mImageDigest, - mImageDigestSize + ImageDigest, + ImageDigestSize ); if (VerifyStatus) { // @@ -1607,528 +1534,52 @@ IsAllowedByDb ( } /** - Provide verification service for signed images, which include both signature validation - and platform policy control. For signature types, both UEFI WIN_CERTIFICATE_UEFI_GUID and - MSFT Authenticode type signatures are supported. - - In this implementation, only verify external executables when in USER MODE. - Executables from FV is bypass, so pass in AuthenticationStatus is ignored. - - The image verification policy is: - If the image is signed, - At least one valid signature or at least one hash value of the image must match a record - in the security database "db", and no valid signature nor any hash value of the image may - be reflected in the security database "dbx". - Otherwise, the image is not signed, - The hash value of the image must match a record in the security database "db", and - not be reflected in the security data base "dbx". + Check whether signature is in specified database. - Caution: This function may receive untrusted input. - PE/COFF image is external input, so this function will validate its data structure - within this image buffer before use. + @param[in] FileBase Pointer to the file content. + @param[in] FileSize Size of the file. + @param[inout] SecDataDir POinter to a pointer to the Image Security Directory. - @param[in] AuthenticationStatus - This is the authentication status returned from the security - measurement services for the input file. - @param[in] File This is a pointer to the device path of the file that is - being dispatched. This will optionally be used for logging. - @param[in] FileBuffer File buffer matches the input file device path. - @param[in] FileSize Size of File buffer matches the input file device path. - @param[in] BootPolicy A boot policy that was used to call LoadImage() UEFI service. - - @retval EFI_SUCCESS The file specified by DevicePath and non-NULL - FileBuffer did authenticate, and the platform policy dictates - that the DXE Foundation may use the file. - @retval EFI_SUCCESS The device path specified by NULL device path DevicePath - and non-NULL FileBuffer did authenticate, and the platform - policy dictates that the DXE Foundation may execute the image in - FileBuffer. - @retval EFI_SECURITY_VIOLATION The file specified by File did not authenticate, and - the platform policy dictates that File should be placed - in the untrusted state. The image has been added to the file - execution table. - @retval EFI_ACCESS_DENIED The file specified by File and FileBuffer did not - authenticate, and the platform policy dictates that the DXE - Foundation may not use File. The image has - been added to the file execution table. + @retval EFI_SUCCESS Parsed the image without any error. + @retval Others Error occurred in the image parsing. **/ EFI_STATUS -EFIAPI -DxeImageVerificationHandler ( - IN UINT32 AuthenticationStatus, - IN CONST EFI_DEVICE_PATH_PROTOCOL *File OPTIONAL, - IN VOID *FileBuffer, - IN UINTN FileSize, - IN BOOLEAN BootPolicy +GetImageSecDataDir ( + IN VOID *FileBase, + IN UINTN FileSize, + IN OUT EFI_IMAGE_DATA_DIRECTORY **SecDataDir ) { - EFI_IMAGE_DOS_HEADER *DosHdr; - BOOLEAN IsVerified; - EFI_SIGNATURE_LIST *SignatureList; - UINTN SignatureListSize; - EFI_SIGNATURE_DATA *Signature; - EFI_IMAGE_EXECUTION_ACTION Action; - WIN_CERTIFICATE *WinCertificate; - UINT32 Policy; - UINT8 SecureBoot; - UINTN SecureBootSize; - PE_COFF_LOADER_IMAGE_CONTEXT ImageContext; - UINT32 NumberOfRvaAndSizes; - WIN_CERTIFICATE_EFI_PKCS *PkcsCertData; - WIN_CERTIFICATE_UEFI_GUID *WinCertUefiGuid; - UINT8 *AuthData; - UINTN AuthDataSize; - EFI_IMAGE_DATA_DIRECTORY *SecDataDir; - UINT32 SecDataDirEnd; - UINT32 SecDataDirLeft; - UINT32 OffSet; - CHAR16 *NameStr; - RETURN_STATUS PeCoffStatus; - EFI_STATUS HashStatus; - EFI_STATUS DbStatus; - EFI_STATUS VarStatus; - UINT32 VarAttr; - BOOLEAN IsFound; - UINT8 HashAlg; - BOOLEAN IsFoundInDatabase; - - SignatureList = NULL; - SignatureListSize = 0; - WinCertificate = NULL; - SecDataDir = NULL; - PkcsCertData = NULL; - Action = EFI_IMAGE_EXECUTION_AUTH_UNTESTED; - IsVerified = FALSE; - IsFound = FALSE; - IsFoundInDatabase = FALSE; - - // - // Check the image type and get policy setting. - // - switch (GetImageType (File)) { - case IMAGE_FROM_FV: - Policy = ALWAYS_EXECUTE; - break; - - case IMAGE_FROM_OPTION_ROM: - Policy = PcdGet32 (PcdOptionRomImageVerificationPolicy); - break; - - case IMAGE_FROM_REMOVABLE_MEDIA: - Policy = PcdGet32 (PcdRemovableMediaImageVerificationPolicy); - break; - - case IMAGE_FROM_FIXED_MEDIA: - Policy = PcdGet32 (PcdFixedMediaImageVerificationPolicy); - break; - - default: - Policy = DENY_EXECUTE_ON_SECURITY_VIOLATION; - break; - } - - // - // If policy is always/never execute, return directly. - // - if (Policy == ALWAYS_EXECUTE) { - return EFI_SUCCESS; - } - - if (Policy == NEVER_EXECUTE) { - return EFI_ACCESS_DENIED; - } - - // - // The policy QUERY_USER_ON_SECURITY_VIOLATION and ALLOW_EXECUTE_ON_SECURITY_VIOLATION - // violates the UEFI spec and has been removed. - // - ASSERT (Policy != QUERY_USER_ON_SECURITY_VIOLATION && Policy != ALLOW_EXECUTE_ON_SECURITY_VIOLATION); - if ((Policy == QUERY_USER_ON_SECURITY_VIOLATION) || (Policy == ALLOW_EXECUTE_ON_SECURITY_VIOLATION)) { - CpuDeadLoop (); - } - - SecureBootSize = sizeof (SecureBoot); - VarStatus = gRT->GetVariable (EFI_SECURE_BOOT_MODE_NAME, &gEfiGlobalVariableGuid, &VarAttr, &SecureBootSize, &SecureBoot); - // - // Skip verification if SecureBoot variable doesn't exist. - // - if (VarStatus == EFI_NOT_FOUND) { - return EFI_SUCCESS; - } - - // - // Skip verification if SecureBoot is disabled but not AuditMode - // - if ((VarStatus == EFI_SUCCESS) && - (VarAttr == (EFI_VARIABLE_BOOTSERVICE_ACCESS | - EFI_VARIABLE_RUNTIME_ACCESS)) && - (SecureBoot == SECURE_BOOT_MODE_DISABLE)) - { - return EFI_SUCCESS; - } - - // - // Read the Dos header. - // - if (FileBuffer == NULL) { - return EFI_ACCESS_DENIED; - } - - mImageBase = (UINT8 *)FileBuffer; - mImageSize = FileSize; - - ZeroMem (&ImageContext, sizeof (ImageContext)); - ImageContext.Handle = (VOID *)FileBuffer; - ImageContext.ImageRead = (PE_COFF_LOADER_READ_FILE)DxeImageVerificationLibImageRead; + UINT32 NumberOfRvaAndSizes; + EFI_IMAGE_OPTIONAL_HEADER_UNION *NtHeader; - // - // Get information about the image being loaded - // - PeCoffStatus = PeCoffLoaderGetImageInfo (&ImageContext); - if (RETURN_ERROR (PeCoffStatus)) { - // - // The information can't be got from the invalid PeImage - // - DEBUG ((DEBUG_INFO, "DxeImageVerificationLib: PeImage invalid. Cannot retrieve image information.\n")); - goto Failed; - } - - DosHdr = (EFI_IMAGE_DOS_HEADER *)mImageBase; - if (DosHdr->e_magic == EFI_IMAGE_DOS_SIGNATURE) { - // - // DOS image header is present, - // so read the PE header after the DOS image header. - // - mPeCoffHeaderOffset = DosHdr->e_lfanew; - } else { - mPeCoffHeaderOffset = 0; + if (SecDataDir == NULL) { + return EFI_INVALID_PARAMETER; } - // - // Check PE/COFF image. - // - mNtHeader.Pe32 = (EFI_IMAGE_NT_HEADERS32 *)(mImageBase + mPeCoffHeaderOffset); - if (mNtHeader.Pe32->Signature != EFI_IMAGE_NT_SIGNATURE) { - // - // It is not a valid Pe/Coff file. - // - DEBUG ((DEBUG_INFO, "DxeImageVerificationLib: Not a valid PE/COFF image.\n")); - goto Failed; + NtHeader = GetImageNtHeader (FileBase, FileSize); + if (NtHeader == NULL) { + return EFI_NOT_FOUND; } - if (mNtHeader.Pe32->OptionalHeader.Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) { + if (NtHeader->Pe32.OptionalHeader.Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) { // // Use PE32 offset. // - NumberOfRvaAndSizes = mNtHeader.Pe32->OptionalHeader.NumberOfRvaAndSizes; + NumberOfRvaAndSizes = NtHeader->Pe32.OptionalHeader.NumberOfRvaAndSizes; if (NumberOfRvaAndSizes > EFI_IMAGE_DIRECTORY_ENTRY_SECURITY) { - SecDataDir = (EFI_IMAGE_DATA_DIRECTORY *)&mNtHeader.Pe32->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY]; + *SecDataDir = (EFI_IMAGE_DATA_DIRECTORY *)&NtHeader->Pe32.OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY]; } } else { // // Use PE32+ offset. // - NumberOfRvaAndSizes = mNtHeader.Pe32Plus->OptionalHeader.NumberOfRvaAndSizes; + NumberOfRvaAndSizes = NtHeader->Pe32Plus.OptionalHeader.NumberOfRvaAndSizes; if (NumberOfRvaAndSizes > EFI_IMAGE_DIRECTORY_ENTRY_SECURITY) { - SecDataDir = (EFI_IMAGE_DATA_DIRECTORY *)&mNtHeader.Pe32Plus->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY]; - } - } - - // - // Start Image Validation. - // - if ((SecDataDir == NULL) || (SecDataDir->Size == 0)) { - // - // This image is not signed. The hash value of the image must match a record in the security database "db", - // and not be reflected in the security data base "dbx". - // - HashAlg = sizeof (mHash) / sizeof (HASH_TABLE); - while (HashAlg > 0) { - HashAlg--; - if ((mHash[HashAlg].GetContextSize == NULL) || (mHash[HashAlg].HashInit == NULL) || (mHash[HashAlg].HashUpdate == NULL) || (mHash[HashAlg].HashFinal == NULL)) { - continue; - } - - if (!HashPeImage (HashAlg)) { - continue; - } - - DbStatus = IsSignatureFoundInDatabase ( - EFI_IMAGE_SECURITY_DATABASE1, - mImageDigest, - &mCertType, - mImageDigestSize, - &IsFound - ); - if (EFI_ERROR (DbStatus) || IsFound) { - // - // Image Hash is in forbidden database (DBX). - // - DEBUG ((DEBUG_INFO, "DxeImageVerificationLib: Image is not signed and %s hash of image is forbidden by DBX.\n", mHashTypeStr)); - goto Failed; - } - - DbStatus = IsSignatureFoundInDatabase ( - EFI_IMAGE_SECURITY_DATABASE, - mImageDigest, - &mCertType, - mImageDigestSize, - &IsFound - ); - if (!EFI_ERROR (DbStatus) && IsFound) { - // - // Image Hash is in allowed database (DB). - // - IsFoundInDatabase = TRUE; - } - } - - if (IsFoundInDatabase) { - return EFI_SUCCESS; - } - - // - // Image Hash is not found in both forbidden and allowed database. - // - DEBUG ((DEBUG_INFO, "DxeImageVerificationLib: Image is not signed and %s hash of image is not found in DB/DBX.\n", mHashTypeStr)); - goto Failed; - } - - // - // Verify the signature of the image, multiple signatures are allowed as per PE/COFF Section 4.7 - // "Attribute Certificate Table". - // The first certificate starts at offset (SecDataDir->VirtualAddress) from the start of the file. - // - SecDataDirEnd = SecDataDir->VirtualAddress + SecDataDir->Size; - for (OffSet = SecDataDir->VirtualAddress; - OffSet < SecDataDirEnd; - OffSet += (WinCertificate->dwLength + ALIGN_SIZE (WinCertificate->dwLength))) - { - SecDataDirLeft = SecDataDirEnd - OffSet; - if (SecDataDirLeft <= sizeof (WIN_CERTIFICATE)) { - break; - } - - WinCertificate = (WIN_CERTIFICATE *)(mImageBase + OffSet); - if ((SecDataDirLeft < WinCertificate->dwLength) || - (SecDataDirLeft - WinCertificate->dwLength < - ALIGN_SIZE (WinCertificate->dwLength))) - { - break; - } - - // - // Verify the image's Authenticode signature, only DER-encoded PKCS#7 signed data is supported. - // - if (WinCertificate->wCertificateType == WIN_CERT_TYPE_PKCS_SIGNED_DATA) { - // - // The certificate is formatted as WIN_CERTIFICATE_EFI_PKCS which is described in the - // Authenticode specification. - // - PkcsCertData = (WIN_CERTIFICATE_EFI_PKCS *)WinCertificate; - if (PkcsCertData->Hdr.dwLength <= sizeof (PkcsCertData->Hdr)) { - break; - } - - AuthData = PkcsCertData->CertData; - AuthDataSize = PkcsCertData->Hdr.dwLength - sizeof (PkcsCertData->Hdr); - } else if (WinCertificate->wCertificateType == WIN_CERT_TYPE_EFI_GUID) { - // - // The certificate is formatted as WIN_CERTIFICATE_UEFI_GUID which is described in UEFI Spec. - // - WinCertUefiGuid = (WIN_CERTIFICATE_UEFI_GUID *)WinCertificate; - if (WinCertUefiGuid->Hdr.dwLength <= OFFSET_OF (WIN_CERTIFICATE_UEFI_GUID, CertData)) { - break; - } - - if (!CompareGuid (&WinCertUefiGuid->CertType, &gEfiCertPkcs7Guid)) { - continue; - } - - AuthData = WinCertUefiGuid->CertData; - AuthDataSize = WinCertUefiGuid->Hdr.dwLength - OFFSET_OF (WIN_CERTIFICATE_UEFI_GUID, CertData); - } else { - if (WinCertificate->dwLength < sizeof (WIN_CERTIFICATE)) { - break; - } - - continue; - } - - HashStatus = HashPeImageByType (AuthData, AuthDataSize); - if (EFI_ERROR (HashStatus)) { - continue; - } - - // - // Check the digital signature against the revoked certificate in forbidden database (dbx). - // - if (IsForbiddenByDbx (AuthData, AuthDataSize)) { - Action = EFI_IMAGE_EXECUTION_AUTH_SIG_FAILED; - IsVerified = FALSE; - break; - } - - // - // Check the digital signature against the valid certificate in allowed database (db). - // - if (!IsVerified) { - if (IsAllowedByDb (AuthData, AuthDataSize)) { - IsVerified = TRUE; - } - } - - // - // Check the image's hash value. - // - DbStatus = IsSignatureFoundInDatabase ( - EFI_IMAGE_SECURITY_DATABASE1, - mImageDigest, - &mCertType, - mImageDigestSize, - &IsFound - ); - if (EFI_ERROR (DbStatus) || IsFound) { - Action = EFI_IMAGE_EXECUTION_AUTH_SIG_FOUND; - DEBUG ((DEBUG_INFO, "DxeImageVerificationLib: Image is signed but %s hash of image is found in DBX.\n", mHashTypeStr)); - IsVerified = FALSE; - break; - } - - if (!IsVerified) { - DbStatus = IsSignatureFoundInDatabase ( - EFI_IMAGE_SECURITY_DATABASE, - mImageDigest, - &mCertType, - mImageDigestSize, - &IsFound - ); - if (!EFI_ERROR (DbStatus) && IsFound) { - IsVerified = TRUE; - } else { - Action = EFI_IMAGE_EXECUTION_AUTH_SIG_NOT_FOUND; - DEBUG ((DEBUG_INFO, "DxeImageVerificationLib: Image is signed but signature is not allowed by DB and %s hash of image is not found in DB/DBX.\n", mHashTypeStr)); - } - } - } - - if (OffSet != SecDataDirEnd) { - // - // The Size in Certificate Table or the attribute certificate table is corrupted. - // - IsVerified = FALSE; - } - - if (IsVerified) { - return EFI_SUCCESS; - } - - if ((Action == EFI_IMAGE_EXECUTION_AUTH_SIG_FAILED) || (Action == EFI_IMAGE_EXECUTION_AUTH_SIG_FOUND)) { - // - // Get image hash value as signature of executable. - // - SignatureListSize = sizeof (EFI_SIGNATURE_LIST) + sizeof (EFI_SIGNATURE_DATA) - 1 + mImageDigestSize; - SignatureList = (EFI_SIGNATURE_LIST *)AllocateZeroPool (SignatureListSize); - if (SignatureList == NULL) { - SignatureListSize = 0; - goto Failed; + *SecDataDir = (EFI_IMAGE_DATA_DIRECTORY *)&NtHeader->Pe32Plus.OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY]; } - - SignatureList->SignatureHeaderSize = 0; - SignatureList->SignatureListSize = (UINT32)SignatureListSize; - SignatureList->SignatureSize = (UINT32)(sizeof (EFI_SIGNATURE_DATA) - 1 + mImageDigestSize); - CopyMem (&SignatureList->SignatureType, &mCertType, sizeof (EFI_GUID)); - Signature = (EFI_SIGNATURE_DATA *)((UINT8 *)SignatureList + sizeof (EFI_SIGNATURE_LIST)); - CopyMem (Signature->SignatureData, mImageDigest, mImageDigestSize); - } - -Failed: - // - // Policy decides to defer or reject the image; add its information in image - // executable information table in either case. - // - NameStr = ConvertDevicePathToText (File, FALSE, TRUE); - AddImageExeInfo (Action, NameStr, File, SignatureList, SignatureListSize); - if (NameStr != NULL) { - DEBUG ((DEBUG_INFO, "The image doesn't pass verification: %s\n", NameStr)); - FreePool (NameStr); - } - - if (SignatureList != NULL) { - FreePool (SignatureList); - } - - if (Policy == DEFER_EXECUTE_ON_SECURITY_VIOLATION) { - return EFI_SECURITY_VIOLATION; - } - - return EFI_ACCESS_DENIED; -} - -/** - On Ready To Boot Services Event notification handler. - - Add the image execution information table if it is not in system configuration table. - - @param[in] Event Event whose notification function is being invoked - @param[in] Context Pointer to the notification function's context - -**/ -VOID -EFIAPI -OnReadyToBoot ( - IN EFI_EVENT Event, - IN VOID *Context - ) -{ - EFI_IMAGE_EXECUTION_INFO_TABLE *ImageExeInfoTable; - UINTN ImageExeInfoTableSize; - - EfiGetSystemConfigurationTable (&gEfiImageSecurityDatabaseGuid, (VOID **)&ImageExeInfoTable); - if (ImageExeInfoTable != NULL) { - return; - } - - ImageExeInfoTableSize = sizeof (EFI_IMAGE_EXECUTION_INFO_TABLE); - ImageExeInfoTable = (EFI_IMAGE_EXECUTION_INFO_TABLE *)AllocateRuntimePool (ImageExeInfoTableSize); - if (ImageExeInfoTable == NULL) { - return; } - ImageExeInfoTable->NumberOfImages = 0; - gBS->InstallConfigurationTable (&gEfiImageSecurityDatabaseGuid, (VOID *)ImageExeInfoTable); -} - -/** - Register security measurement handler. - - @param ImageHandle ImageHandle of the loaded driver. - @param SystemTable Pointer to the EFI System Table. - - @retval EFI_SUCCESS The handlers were registered successfully. -**/ -EFI_STATUS -EFIAPI -DxeImageVerificationLibConstructor ( - IN EFI_HANDLE ImageHandle, - IN EFI_SYSTEM_TABLE *SystemTable - ) -{ - EFI_EVENT Event; - - // - // Register the event to publish the image execution table. - // - EfiCreateEventReadyToBootEx ( - TPL_CALLBACK, - OnReadyToBoot, - NULL, - &Event - ); - - return RegisterSecurity2Handler ( - DxeImageVerificationHandler, - EFI_AUTH_OPERATION_VERIFY_IMAGE | EFI_AUTH_OPERATION_IMAGE_REQUIRED - ); + return EFI_SUCCESS; } diff --git a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.inf b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.inf index 1e1a639857..88dd4b46ff 100644 --- a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.inf +++ b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.inf @@ -17,11 +17,10 @@ INF_VERSION = 0x00010005 BASE_NAME = DxeImageVerificationLib MODULE_UNI_FILE = DxeImageVerificationLib.uni - FILE_GUID = 0CA970E1-43FA-4402-BC0A-81AF336BFFD6 + FILE_GUID = B9EB99BB-D7BE-466C-96DB-5994FBA95FB3 MODULE_TYPE = DXE_DRIVER VERSION_STRING = 1.0 - LIBRARY_CLASS = NULL|DXE_DRIVER DXE_RUNTIME_DRIVER DXE_SMM_DRIVER UEFI_APPLICATION UEFI_DRIVER - CONSTRUCTOR = DxeImageVerificationLibConstructor + LIBRARY_CLASS = DxeImageVerificationLib|DXE_DRIVER DXE_RUNTIME_DRIVER DXE_SMM_DRIVER UEFI_APPLICATION UEFI_DRIVER # # The following information is for reference only and not required by the build tools. @@ -31,7 +30,7 @@ [Sources] DxeImageVerificationLib.c - DxeImageVerificationLib.h + ImageVerificationLibInternal.h Measurement.c [Packages] @@ -88,8 +87,3 @@ gEfiCertX509Sha384Guid ## SOMETIMES_CONSUMES ## GUID # Unique ID for the type of the signature. gEfiCertX509Sha512Guid ## SOMETIMES_CONSUMES ## GUID # Unique ID for the type of the signature. gEfiCertPkcs7Guid ## SOMETIMES_CONSUMES ## GUID # Unique ID for the type of the certificate. - -[Pcd] - gEfiSecurityPkgTokenSpaceGuid.PcdOptionRomImageVerificationPolicy ## SOMETIMES_CONSUMES - gEfiSecurityPkgTokenSpaceGuid.PcdRemovableMediaImageVerificationPolicy ## SOMETIMES_CONSUMES - gEfiSecurityPkgTokenSpaceGuid.PcdFixedMediaImageVerificationPolicy ## SOMETIMES_CONSUMES diff --git a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.h b/SecurityPkg/Library/DxeImageVerificationLib/ImageVerificationLibInternal.h similarity index 87% rename from SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.h rename to SecurityPkg/Library/DxeImageVerificationLib/ImageVerificationLibInternal.h index 53fe34358c..c1b1d28458 100644 --- a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.h +++ b/SecurityPkg/Library/DxeImageVerificationLib/ImageVerificationLibInternal.h @@ -19,6 +19,7 @@ SPDX-License-Identifier: BSD-2-Clause-Patent #include #include #include +#include #include #include #include @@ -59,29 +60,6 @@ SPDX-License-Identifier: BSD-2-Clause-Patent #define DENY_EXECUTE_ON_SECURITY_VIOLATION 0x00000004 #define QUERY_USER_ON_SECURITY_VIOLATION 0x00000005 -// -// Support hash types -// -#define HASHALG_SHA1 0x00000000 -#define HASHALG_SHA224 0x00000001 -#define HASHALG_SHA256 0x00000002 -#define HASHALG_SHA384 0x00000003 -#define HASHALG_SHA512 0x00000004 -#define HASHALG_MAX 0x00000005 - -// -// Set max digest size as SHA512 Output (64 bytes) by far -// -#define MAX_DIGEST_SIZE SHA512_DIGEST_SIZE -// -// -// PKCS7 Certificate definition -// -typedef struct { - WIN_CERTIFICATE Hdr; - UINT8 CertData[1]; -} WIN_CERTIFICATE_EFI_PKCS; - /** Retrieves the size, in bytes, of the context buffer required for hash operations. diff --git a/SecurityPkg/SecurityPkg.dec b/SecurityPkg/SecurityPkg.dec index bc792569d7..17e8c7772c 100644 --- a/SecurityPkg/SecurityPkg.dec +++ b/SecurityPkg/SecurityPkg.dec @@ -104,6 +104,9 @@ ## @libraryclass Perform SPDM (following SPDM spec) and measure data to TPM (following TCG PFP spec). ## SpdmSecurityLib|Include/Library/SpdmSecurityLib.h + ## @libraryclass Provides functions to verify images and check against signature databases. + # + ImageVerificationLib|Include/Library/ImageVerificationLib.h [Guids] ## Security package token space guid. diff --git a/SecurityPkg/SecurityPkg.dsc b/SecurityPkg/SecurityPkg.dsc index ea6a14c328..dae78e65e0 100644 --- a/SecurityPkg/SecurityPkg.dsc +++ b/SecurityPkg/SecurityPkg.dsc @@ -210,6 +210,7 @@ [Components] SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.inf + SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationHandler.inf SecurityPkg/Library/DxeImageAuthenticationStatusLib/DxeImageAuthenticationStatusLib.inf # diff --git a/SecurityPkg/VariableAuthenticated/SecureBootConfigDxe/SecureBootConfig.vfr b/SecurityPkg/VariableAuthenticated/SecureBootConfigDxe/SecureBootConfig.vfr index 85517ac8c6..c2437a75e9 100644 --- a/SecurityPkg/VariableAuthenticated/SecureBootConfigDxe/SecureBootConfig.vfr +++ b/SecurityPkg/VariableAuthenticated/SecureBootConfigDxe/SecureBootConfig.vfr @@ -570,10 +570,16 @@ formset varid = SECUREBOOT_CONFIGURATION.CertificateFormat, prompt = STRING_TOKEN(STR_DBX_CERTIFICATE_FORMAT_PROMPT), help = STRING_TOKEN(STR_DBX_CERTIFICATE_FORMAT_HELP), - option text = STRING_TOKEN(STR_DBX_CERTIFICATE_FORMAT_SHA256), value = 0x1, flags = DEFAULT; - option text = STRING_TOKEN(STR_DBX_CERTIFICATE_FORMAT_SHA384), value = 0x2, flags = 0; - option text = STRING_TOKEN(STR_DBX_CERTIFICATE_FORMAT_SHA512), value = 0x3, flags = 0; - option text = STRING_TOKEN(STR_DBX_CERTIFICATE_FORMAT_RAW), value = 0x4, flags = 0; + disableif TRUE; // Make it depend on DISABLE_SHA1_DEPRECATED_INTERFACES? + option text = STRING_TOKEN(STR_DBX_CERTIFICATE_FORMAT_SHA1), value = 0x0, flags = 0; + endif; + suppressif TRUE; // SHA224 not implemented + option text = STRING_TOKEN(STR_DBX_CERTIFICATE_FORMAT_SHA224), value = 0x1, flags = 0; + endif; + option text = STRING_TOKEN(STR_DBX_CERTIFICATE_FORMAT_SHA256), value = 0x2, flags = DEFAULT; + option text = STRING_TOKEN(STR_DBX_CERTIFICATE_FORMAT_SHA384), value = 0x3, flags = 0; + option text = STRING_TOKEN(STR_DBX_CERTIFICATE_FORMAT_SHA512), value = 0x4, flags = 0; + option text = STRING_TOKEN(STR_DBX_CERTIFICATE_FORMAT_RAW), value = 0x6, flags = 0; endoneof; endif; diff --git a/SecurityPkg/VariableAuthenticated/SecureBootConfigDxe/SecureBootConfigDxe.inf b/SecurityPkg/VariableAuthenticated/SecureBootConfigDxe/SecureBootConfigDxe.inf index 28734b6b42..d85b300871 100644 --- a/SecurityPkg/VariableAuthenticated/SecureBootConfigDxe/SecureBootConfigDxe.inf +++ b/SecurityPkg/VariableAuthenticated/SecureBootConfigDxe/SecureBootConfigDxe.inf @@ -59,6 +59,7 @@ PeCoffLib SecureBootVariableLib SecureBootVariableProvisionLib + DxeImageVerificationLib [FixedPcd] gEfiSecurityPkgTokenSpaceGuid.PcdSecureBootDefaultEnable diff --git a/SecurityPkg/VariableAuthenticated/SecureBootConfigDxe/SecureBootConfigImpl.c b/SecurityPkg/VariableAuthenticated/SecureBootConfigDxe/SecureBootConfigImpl.c index e33df6842e..5a17bef354 100644 --- a/SecurityPkg/VariableAuthenticated/SecureBootConfigDxe/SecureBootConfigImpl.c +++ b/SecurityPkg/VariableAuthenticated/SecureBootConfigDxe/SecureBootConfigImpl.c @@ -14,6 +14,7 @@ SPDX-License-Identifier: BSD-2-Clause-Patent #include #include #include +#include #include #include @@ -61,39 +62,13 @@ HII_VENDOR_DEVICE_PATH mSecureBootHiiVendorDevicePath = { BOOLEAN mIsEnterSecureBootForm = FALSE; BOOLEAN mResetSvBootState = FALSE; -// -// OID ASN.1 Value for Hash Algorithms -// -UINT8 mHashOidValue[] = { - 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x02, 0x05, // OBJ_md5 - 0x2B, 0x0E, 0x03, 0x02, 0x1A, // OBJ_sha1 - 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x04, // OBJ_sha224 - 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, // OBJ_sha256 - 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x02, // OBJ_sha384 - 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, // OBJ_sha512 -}; - -HASH_TABLE mHash[] = { - { L"SHA224", 28, &mHashOidValue[13], 9, NULL, NULL, NULL, NULL }, - { L"SHA256", 32, &mHashOidValue[22], 9, Sha256GetContextSize, Sha256Init, Sha256Update, Sha256Final }, - { L"SHA384", 48, &mHashOidValue[31], 9, Sha384GetContextSize, Sha384Init, Sha384Update, Sha384Final }, - { L"SHA512", 64, &mHashOidValue[40], 9, Sha512GetContextSize, Sha512Init, Sha512Update, Sha512Final } -}; - // // Variable Definitions // -UINT32 mPeCoffHeaderOffset = 0; -WIN_CERTIFICATE *mCertificate = NULL; -IMAGE_TYPE mImageType; -UINT8 *mImageBase = NULL; -UINTN mImageSize = 0; -UINT8 mImageDigest[MAX_DIGEST_SIZE]; -UINTN mImageDigestSize; -EFI_GUID mCertType; -EFI_IMAGE_SECURITY_DATA_DIRECTORY *mSecDataDir = NULL; -EFI_IMAGE_OPTIONAL_HEADER_PTR_UNION mNtHeader; - +WIN_CERTIFICATE *mCertificate = NULL; +EFI_IMAGE_SECURITY_DATA_DIRECTORY *mSecDataDir = NULL; +UINT8 *mImageBase = NULL; +UINTN mImageSize = 0; // // Possible DER-encoded certificate file suffixes, end with NULL pointer. // @@ -1144,278 +1119,6 @@ EnrollX509toSigDB ( return Status; } -/** - Check whether signature is in specified database. - - @param[in] VariableName Name of database variable that is searched in. - @param[in] Signature Pointer to signature that is searched for. - @param[in] SignatureSize Size of Signature. - - @return TRUE Found the signature in the variable database. - @return FALSE Not found the signature in the variable database. - -**/ -BOOLEAN -IsSignatureFoundInDatabase ( - IN CHAR16 *VariableName, - IN UINT8 *Signature, - IN UINTN SignatureSize - ) -{ - EFI_STATUS Status; - EFI_SIGNATURE_LIST *CertList; - EFI_SIGNATURE_DATA *Cert; - UINTN DataSize; - UINT8 *Data; - UINTN Index; - UINTN CertCount; - BOOLEAN IsFound; - - // - // Read signature database variable. - // - IsFound = FALSE; - Data = NULL; - DataSize = 0; - Status = gRT->GetVariable (VariableName, &gEfiImageSecurityDatabaseGuid, NULL, &DataSize, NULL); - if (Status != EFI_BUFFER_TOO_SMALL) { - return FALSE; - } - - Data = (UINT8 *)AllocateZeroPool (DataSize); - if (Data == NULL) { - return FALSE; - } - - Status = gRT->GetVariable (VariableName, &gEfiImageSecurityDatabaseGuid, NULL, &DataSize, Data); - if (EFI_ERROR (Status)) { - goto Done; - } - - // - // Enumerate all signature data in SigDB to check if signature exists for executable. - // - CertList = (EFI_SIGNATURE_LIST *)Data; - while ((DataSize > 0) && (DataSize >= CertList->SignatureListSize)) { - CertCount = (CertList->SignatureListSize - sizeof (EFI_SIGNATURE_LIST) - CertList->SignatureHeaderSize) / CertList->SignatureSize; - Cert = (EFI_SIGNATURE_DATA *)((UINT8 *)CertList + sizeof (EFI_SIGNATURE_LIST) + CertList->SignatureHeaderSize); - if ((CertList->SignatureSize == sizeof (EFI_SIGNATURE_DATA) - 1 + SignatureSize) && (CompareGuid (&CertList->SignatureType, &gEfiCertX509Guid))) { - for (Index = 0; Index < CertCount; Index++) { - if (CompareMem (Cert->SignatureData, Signature, SignatureSize) == 0) { - // - // Find the signature in database. - // - IsFound = TRUE; - break; - } - - Cert = (EFI_SIGNATURE_DATA *)((UINT8 *)Cert + CertList->SignatureSize); - } - - if (IsFound) { - break; - } - } - - DataSize -= CertList->SignatureListSize; - CertList = (EFI_SIGNATURE_LIST *)((UINT8 *)CertList + CertList->SignatureListSize); - } - -Done: - if (Data != NULL) { - FreePool (Data); - } - - return IsFound; -} - -/** - Calculate the hash of a certificate data with the specified hash algorithm. - - @param[in] CertData The certificate data to be hashed. - @param[in] CertSize The certificate size in bytes. - @param[in] HashAlg The specified hash algorithm. - @param[out] CertHash The output digest of the certificate - - @retval TRUE Successfully got the hash of the CertData. - @retval FALSE Failed to get the hash of CertData. - -**/ -BOOLEAN -CalculateCertHash ( - IN UINT8 *CertData, - IN UINTN CertSize, - IN UINT32 HashAlg, - OUT UINT8 *CertHash - ) -{ - BOOLEAN Status; - VOID *HashCtx; - UINTN CtxSize; - UINT8 *TBSCert; - UINTN TBSCertSize; - - HashCtx = NULL; - Status = FALSE; - - if (HashAlg >= HASHALG_MAX) { - return FALSE; - } - - // - // Retrieve the TBSCertificate for Hash Calculation. - // - if (!X509GetTBSCert (CertData, CertSize, &TBSCert, &TBSCertSize)) { - return FALSE; - } - - // - // 1. Initialize context of hash. - // - CtxSize = mHash[HashAlg].GetContextSize (); - HashCtx = AllocatePool (CtxSize); - ASSERT (HashCtx != NULL); - - // - // 2. Initialize a hash context. - // - Status = mHash[HashAlg].HashInit (HashCtx); - if (!Status) { - goto Done; - } - - // - // 3. Calculate the hash. - // - Status = mHash[HashAlg].HashUpdate (HashCtx, TBSCert, TBSCertSize); - if (!Status) { - goto Done; - } - - // - // 4. Get the hash result. - // - ZeroMem (CertHash, mHash[HashAlg].DigestLength); - Status = mHash[HashAlg].HashFinal (HashCtx, CertHash); - -Done: - if (HashCtx != NULL) { - FreePool (HashCtx); - } - - return Status; -} - -/** - Check whether the hash of an X.509 certificate is in forbidden database (DBX). - - @param[in] Certificate Pointer to X.509 Certificate that is searched for. - @param[in] CertSize Size of X.509 Certificate. - - @return TRUE Found the certificate hash in the forbidden database. - @return FALSE Certificate hash is Not found in the forbidden database. - -**/ -BOOLEAN -IsCertHashFoundInDbx ( - IN UINT8 *Certificate, - IN UINTN CertSize - ) -{ - BOOLEAN IsFound; - EFI_STATUS Status; - EFI_SIGNATURE_LIST *DbxList; - EFI_SIGNATURE_DATA *CertHash; - UINTN CertHashCount; - UINTN Index; - UINT32 HashAlg; - UINT8 CertDigest[MAX_DIGEST_SIZE]; - UINT8 *DbxCertHash; - UINTN SiglistHeaderSize; - UINT8 *Data; - UINTN DataSize; - - IsFound = FALSE; - HashAlg = HASHALG_MAX; - Data = NULL; - - // - // Read signature database variable. - // - DataSize = 0; - Status = gRT->GetVariable (EFI_IMAGE_SECURITY_DATABASE1, &gEfiImageSecurityDatabaseGuid, NULL, &DataSize, NULL); - if (Status != EFI_BUFFER_TOO_SMALL) { - return FALSE; - } - - Data = (UINT8 *)AllocateZeroPool (DataSize); - if (Data == NULL) { - return FALSE; - } - - Status = gRT->GetVariable (EFI_IMAGE_SECURITY_DATABASE1, &gEfiImageSecurityDatabaseGuid, NULL, &DataSize, Data); - if (EFI_ERROR (Status)) { - goto Done; - } - - // - // Check whether the certificate hash exists in the forbidden database. - // - DbxList = (EFI_SIGNATURE_LIST *)Data; - while ((DataSize > 0) && (DataSize >= DbxList->SignatureListSize)) { - // - // Determine Hash Algorithm of Certificate in the forbidden database. - // - if (CompareGuid (&DbxList->SignatureType, &gEfiCertX509Sha256Guid)) { - HashAlg = HASHALG_SHA256; - } else if (CompareGuid (&DbxList->SignatureType, &gEfiCertX509Sha384Guid)) { - HashAlg = HASHALG_SHA384; - } else if (CompareGuid (&DbxList->SignatureType, &gEfiCertX509Sha512Guid)) { - HashAlg = HASHALG_SHA512; - } else { - DataSize -= DbxList->SignatureListSize; - DbxList = (EFI_SIGNATURE_LIST *)((UINT8 *)DbxList + DbxList->SignatureListSize); - continue; - } - - // - // Calculate the hash value of current db certificate for comparision. - // - if (!CalculateCertHash (Certificate, CertSize, HashAlg, CertDigest)) { - goto Done; - } - - SiglistHeaderSize = sizeof (EFI_SIGNATURE_LIST) + DbxList->SignatureHeaderSize; - CertHash = (EFI_SIGNATURE_DATA *)((UINT8 *)DbxList + SiglistHeaderSize); - CertHashCount = (DbxList->SignatureListSize - SiglistHeaderSize) / DbxList->SignatureSize; - for (Index = 0; Index < CertHashCount; Index++) { - // - // Iterate each Signature Data Node within this CertList for verify. - // - DbxCertHash = CertHash->SignatureData; - if (CompareMem (DbxCertHash, CertDigest, mHash[HashAlg].DigestLength) == 0) { - // - // Hash of Certificate is found in forbidden database. - // - IsFound = TRUE; - goto Done; - } - - CertHash = (EFI_SIGNATURE_DATA *)((UINT8 *)CertHash + DbxList->SignatureSize); - } - - DataSize -= DbxList->SignatureListSize; - DbxList = (EFI_SIGNATURE_LIST *)((UINT8 *)DbxList + DbxList->SignatureListSize); - } - -Done: - if (Data != NULL) { - FreePool (Data); - } - - return IsFound; -} - /** Check whether the signature list exists in given variable data. @@ -1579,20 +1282,20 @@ EnrollX509HashtoSigDB ( // // Allocate memory for Signature and fill the Signature // - SignatureSize = sizeof (EFI_SIGNATURE_DATA) - 1 + sizeof (EFI_TIME) + mHash[HashAlg].DigestLength; + SignatureSize = sizeof (EFI_SIGNATURE_DATA) - 1 + sizeof (EFI_TIME) + GetDigestLength(HashAlg); SignatureData = (EFI_SIGNATURE_DATA *)AllocateZeroPool (SignatureSize); if (SignatureData == NULL) { return EFI_OUT_OF_RESOURCES; } CopyGuid (&SignatureData->SignatureOwner, Private->SignatureGUID); - CopyMem (SignatureData->SignatureData, CertHash, mHash[HashAlg].DigestLength); + CopyMem (SignatureData->SignatureData, CertHash, GetDigestLength(HashAlg)); // // Fill the time. // if (!AlwaysRevocation) { - Time = (EFI_TIME *)(&SignatureData->SignatureData + mHash[HashAlg].DigestLength); + Time = (EFI_TIME *)(&SignatureData->SignatureData + GetDigestLength(HashAlg)); Time->Year = RevocationDate->Year; Time->Month = RevocationDate->Month; Time->Day = RevocationDate->Day; @@ -1727,16 +1430,13 @@ EnrollX509HashtoSigDB ( Check whether a certificate from a file exists in dbx. @param[in] PrivateData The module's private data. - @param[in] VariableName Variable name of signature database, must be - EFI_IMAGE_SECURITY_DATABASE1. @retval TRUE The X509 certificate is found in dbx successfully. @retval FALSE The X509 certificate is not found in dbx. **/ BOOLEAN IsX509CertInDbx ( - IN SECUREBOOT_CONFIG_PRIVATE_DATA *Private, - IN CHAR16 *VariableName + IN SECUREBOOT_CONFIG_PRIVATE_DATA *Private ) { EFI_STATUS Status; @@ -1763,16 +1463,34 @@ IsX509CertInDbx ( // Check the raw certificate. // IsFound = FALSE; - if (IsSignatureFoundInDatabase (EFI_IMAGE_SECURITY_DATABASE1, X509Data, X509DataSize)) { - IsFound = TRUE; + Status = IsSignatureFoundInDatabase ( + EFI_IMAGE_SECURITY_DATABASE1, + X509Data, + &gEfiCertX509Guid, + X509DataSize, + &IsFound); + if (EFI_ERROR (Status)) { + IsFound = FALSE; + goto ON_EXIT; + } + + if (IsFound) { goto ON_EXIT; } // // Check the hash of certificate. // - if (IsCertHashFoundInDbx (X509Data, X509DataSize)) { - IsFound = TRUE; + IsFound = FALSE; + Status = IsCertHashFoundInDbx ( + X509Data, + X509DataSize, + NULL, + 0, + NULL, + &IsFound); + if (EFI_ERROR (Status)) { + IsFound = FALSE; goto ON_EXIT; } @@ -1784,471 +1502,6 @@ IsX509CertInDbx ( return IsFound; } -/** - Reads contents of a PE/COFF image in memory buffer. - - Caution: This function may receive untrusted input. - PE/COFF image is external input, so this function will make sure the PE/COFF image content - read is within the image buffer. - - @param FileHandle Pointer to the file handle to read the PE/COFF image. - @param FileOffset Offset into the PE/COFF image to begin the read operation. - @param ReadSize On input, the size in bytes of the requested read operation. - On output, the number of bytes actually read. - @param Buffer Output buffer that contains the data read from the PE/COFF image. - - @retval EFI_SUCCESS The specified portion of the PE/COFF image was read and the size -**/ -EFI_STATUS -EFIAPI -SecureBootConfigImageRead ( - IN VOID *FileHandle, - IN UINTN FileOffset, - IN OUT UINTN *ReadSize, - OUT VOID *Buffer - ) -{ - UINTN EndPosition; - - if ((FileHandle == NULL) || (ReadSize == NULL) || (Buffer == NULL)) { - return EFI_INVALID_PARAMETER; - } - - if (MAX_ADDRESS - FileOffset < *ReadSize) { - return EFI_INVALID_PARAMETER; - } - - EndPosition = FileOffset + *ReadSize; - if (EndPosition > mImageSize) { - *ReadSize = (UINT32)(mImageSize - FileOffset); - } - - if (FileOffset >= mImageSize) { - *ReadSize = 0; - } - - CopyMem (Buffer, (UINT8 *)((UINTN)FileHandle + FileOffset), *ReadSize); - - return EFI_SUCCESS; -} - -/** - Load PE/COFF image information into internal buffer and check its validity. - - @retval EFI_SUCCESS Successful - @retval EFI_UNSUPPORTED Invalid PE/COFF file - @retval EFI_ABORTED Serious error occurs, like file I/O error etc. - -**/ -EFI_STATUS -LoadPeImage ( - VOID - ) -{ - EFI_IMAGE_DOS_HEADER *DosHdr; - EFI_IMAGE_NT_HEADERS32 *NtHeader32; - EFI_IMAGE_NT_HEADERS64 *NtHeader64; - PE_COFF_LOADER_IMAGE_CONTEXT ImageContext; - EFI_STATUS Status; - - NtHeader32 = NULL; - NtHeader64 = NULL; - - ZeroMem (&ImageContext, sizeof (ImageContext)); - ImageContext.Handle = (VOID *)mImageBase; - ImageContext.ImageRead = (PE_COFF_LOADER_READ_FILE)SecureBootConfigImageRead; - - // - // Get information about the image being loaded - // - Status = PeCoffLoaderGetImageInfo (&ImageContext); - if (EFI_ERROR (Status)) { - // - // The information can't be got from the invalid PeImage - // - DEBUG ((DEBUG_INFO, "SecureBootConfigDxe: PeImage invalid. \n")); - return Status; - } - - // - // Read the Dos header - // - DosHdr = (EFI_IMAGE_DOS_HEADER *)(mImageBase); - if (DosHdr->e_magic == EFI_IMAGE_DOS_SIGNATURE) { - // - // DOS image header is present, - // So read the PE header after the DOS image header - // - mPeCoffHeaderOffset = DosHdr->e_lfanew; - } else { - mPeCoffHeaderOffset = 0; - } - - // - // Read PE header and check the signature validity and machine compatibility - // - NtHeader32 = (EFI_IMAGE_NT_HEADERS32 *)(mImageBase + mPeCoffHeaderOffset); - if (NtHeader32->Signature != EFI_IMAGE_NT_SIGNATURE) { - return EFI_UNSUPPORTED; - } - - mNtHeader.Pe32 = NtHeader32; - - // - // Check the architecture field of PE header and get the Certificate Data Directory data - // Note the size of FileHeader field is constant for both IA32 and X64 arch - // - if ( (NtHeader32->FileHeader.Machine == EFI_IMAGE_MACHINE_IA32) - || (NtHeader32->FileHeader.Machine == EFI_IMAGE_MACHINE_EBC) - || (NtHeader32->FileHeader.Machine == EFI_IMAGE_MACHINE_ARMTHUMB_MIXED)) - { - // - // 32-bits Architecture - // - mImageType = ImageType_IA32; - mSecDataDir = (EFI_IMAGE_SECURITY_DATA_DIRECTORY *)&(NtHeader32->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY]); - } else if ( (NtHeader32->FileHeader.Machine == EFI_IMAGE_MACHINE_IA64) - || (NtHeader32->FileHeader.Machine == EFI_IMAGE_MACHINE_X64) - || (NtHeader32->FileHeader.Machine == EFI_IMAGE_MACHINE_AARCH64)) - { - // - // 64-bits Architecture - // - mImageType = ImageType_X64; - NtHeader64 = (EFI_IMAGE_NT_HEADERS64 *)(mImageBase + mPeCoffHeaderOffset); - mSecDataDir = (EFI_IMAGE_SECURITY_DATA_DIRECTORY *)&(NtHeader64->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY]); - } else { - return EFI_UNSUPPORTED; - } - - return EFI_SUCCESS; -} - -/** - Calculate hash of Pe/Coff image based on the authenticode image hashing in - PE/COFF Specification 8.0 Appendix A - - Notes: PE/COFF image has been checked by BasePeCoffLib PeCoffLoaderGetImageInfo() in - the function LoadPeImage (). - - @param[in] HashAlg Hash algorithm type. - - @retval TRUE Successfully hash image. - @retval FALSE Fail in hash image. - -**/ -BOOLEAN -HashPeImage ( - IN UINT32 HashAlg - ) -{ - BOOLEAN Status; - EFI_IMAGE_SECTION_HEADER *Section; - VOID *HashCtx; - UINTN CtxSize; - UINT8 *HashBase; - UINTN HashSize; - UINTN SumOfBytesHashed; - EFI_IMAGE_SECTION_HEADER *SectionHeader; - UINTN Index; - UINTN Pos; - - HashCtx = NULL; - SectionHeader = NULL; - Status = FALSE; - - if ((HashAlg >= HASHALG_MAX)) { - return FALSE; - } - - // - // Initialize context of hash. - // - ZeroMem (mImageDigest, MAX_DIGEST_SIZE); - - switch (HashAlg) { - case HASHALG_SHA256: - mImageDigestSize = SHA256_DIGEST_SIZE; - mCertType = gEfiCertSha256Guid; - break; - - case HASHALG_SHA384: - mImageDigestSize = SHA384_DIGEST_SIZE; - mCertType = gEfiCertSha384Guid; - break; - - case HASHALG_SHA512: - mImageDigestSize = SHA512_DIGEST_SIZE; - mCertType = gEfiCertSha512Guid; - break; - - default: - return FALSE; - } - - CtxSize = mHash[HashAlg].GetContextSize (); - - HashCtx = AllocatePool (CtxSize); - ASSERT (HashCtx != NULL); - - // 1. Load the image header into memory. - - // 2. Initialize a SHA hash context. - Status = mHash[HashAlg].HashInit (HashCtx); - if (!Status) { - goto Done; - } - - // - // Measuring PE/COFF Image Header; - // But CheckSum field and SECURITY data directory (certificate) are excluded - // - - // - // 3. Calculate the distance from the base of the image header to the image checksum address. - // 4. Hash the image header from its base to beginning of the image checksum. - // - HashBase = mImageBase; - if (mNtHeader.Pe32->OptionalHeader.Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) { - // - // Use PE32 offset. - // - HashSize = (UINTN)(&mNtHeader.Pe32->OptionalHeader.CheckSum) - (UINTN)HashBase; - } else { - // - // Use PE32+ offset. - // - HashSize = (UINTN)(&mNtHeader.Pe32Plus->OptionalHeader.CheckSum) - (UINTN)HashBase; - } - - Status = mHash[HashAlg].HashUpdate (HashCtx, HashBase, HashSize); - if (!Status) { - goto Done; - } - - // - // 5. Skip over the image checksum (it occupies a single ULONG). - // 6. Get the address of the beginning of the Cert Directory. - // 7. Hash everything from the end of the checksum to the start of the Cert Directory. - // - if (mNtHeader.Pe32->OptionalHeader.Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) { - // - // Use PE32 offset. - // - HashBase = (UINT8 *)&mNtHeader.Pe32->OptionalHeader.CheckSum + sizeof (UINT32); - HashSize = (UINTN)(&mNtHeader.Pe32->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY]) - (UINTN)HashBase; - } else { - // - // Use PE32+ offset. - // - HashBase = (UINT8 *)&mNtHeader.Pe32Plus->OptionalHeader.CheckSum + sizeof (UINT32); - HashSize = (UINTN)(&mNtHeader.Pe32Plus->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY]) - (UINTN)HashBase; - } - - Status = mHash[HashAlg].HashUpdate (HashCtx, HashBase, HashSize); - if (!Status) { - goto Done; - } - - // - // 8. Skip over the Cert Directory. (It is sizeof(IMAGE_DATA_DIRECTORY) bytes.) - // 9. Hash everything from the end of the Cert Directory to the end of image header. - // - if (mNtHeader.Pe32->OptionalHeader.Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) { - // - // Use PE32 offset - // - HashBase = (UINT8 *)&mNtHeader.Pe32->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY + 1]; - HashSize = mNtHeader.Pe32->OptionalHeader.SizeOfHeaders - ((UINTN)(&mNtHeader.Pe32->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY + 1]) - (UINTN)mImageBase); - } else { - // - // Use PE32+ offset. - // - HashBase = (UINT8 *)&mNtHeader.Pe32Plus->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY + 1]; - HashSize = mNtHeader.Pe32Plus->OptionalHeader.SizeOfHeaders - ((UINTN)(&mNtHeader.Pe32Plus->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY + 1]) - (UINTN)mImageBase); - } - - Status = mHash[HashAlg].HashUpdate (HashCtx, HashBase, HashSize); - if (!Status) { - goto Done; - } - - // - // 10. Set the SUM_OF_BYTES_HASHED to the size of the header. - // - if (mNtHeader.Pe32->OptionalHeader.Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) { - // - // Use PE32 offset. - // - SumOfBytesHashed = mNtHeader.Pe32->OptionalHeader.SizeOfHeaders; - } else { - // - // Use PE32+ offset - // - SumOfBytesHashed = mNtHeader.Pe32Plus->OptionalHeader.SizeOfHeaders; - } - - // - // 11. Build a temporary table of pointers to all the IMAGE_SECTION_HEADER - // structures in the image. The 'NumberOfSections' field of the image - // header indicates how big the table should be. Do not include any - // IMAGE_SECTION_HEADERs in the table whose 'SizeOfRawData' field is zero. - // - SectionHeader = (EFI_IMAGE_SECTION_HEADER *)AllocateZeroPool (sizeof (EFI_IMAGE_SECTION_HEADER) * mNtHeader.Pe32->FileHeader.NumberOfSections); - ASSERT (SectionHeader != NULL); - // - // 12. Using the 'PointerToRawData' in the referenced section headers as - // a key, arrange the elements in the table in ascending order. In other - // words, sort the section headers according to the disk-file offset of - // the section. - // - Section = (EFI_IMAGE_SECTION_HEADER *)( - mImageBase + - mPeCoffHeaderOffset + - sizeof (UINT32) + - sizeof (EFI_IMAGE_FILE_HEADER) + - mNtHeader.Pe32->FileHeader.SizeOfOptionalHeader - ); - for (Index = 0; Index < mNtHeader.Pe32->FileHeader.NumberOfSections; Index++) { - Pos = Index; - while ((Pos > 0) && (Section->PointerToRawData < SectionHeader[Pos - 1].PointerToRawData)) { - CopyMem (&SectionHeader[Pos], &SectionHeader[Pos - 1], sizeof (EFI_IMAGE_SECTION_HEADER)); - Pos--; - } - - CopyMem (&SectionHeader[Pos], Section, sizeof (EFI_IMAGE_SECTION_HEADER)); - Section += 1; - } - - // - // 13. Walk through the sorted table, bring the corresponding section - // into memory, and hash the entire section (using the 'SizeOfRawData' - // field in the section header to determine the amount of data to hash). - // 14. Add the section's 'SizeOfRawData' to SUM_OF_BYTES_HASHED . - // 15. Repeat steps 13 and 14 for all the sections in the sorted table. - // - for (Index = 0; Index < mNtHeader.Pe32->FileHeader.NumberOfSections; Index++) { - Section = &SectionHeader[Index]; - if (Section->SizeOfRawData == 0) { - continue; - } - - HashBase = mImageBase + Section->PointerToRawData; - HashSize = (UINTN)Section->SizeOfRawData; - - Status = mHash[HashAlg].HashUpdate (HashCtx, HashBase, HashSize); - if (!Status) { - goto Done; - } - - SumOfBytesHashed += HashSize; - } - - // - // 16. If the file size is greater than SUM_OF_BYTES_HASHED, there is extra - // data in the file that needs to be added to the hash. This data begins - // at file offset SUM_OF_BYTES_HASHED and its length is: - // FileSize - (CertDirectory->Size) - // - if (mImageSize > SumOfBytesHashed) { - HashBase = mImageBase + SumOfBytesHashed; - if (mNtHeader.Pe32->OptionalHeader.Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) { - // - // Use PE32 offset. - // - HashSize = (UINTN)( - mImageSize - - mNtHeader.Pe32->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY].Size - - SumOfBytesHashed); - } else { - // - // Use PE32+ offset. - // - HashSize = (UINTN)( - mImageSize - - mNtHeader.Pe32Plus->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY].Size - - SumOfBytesHashed); - } - - Status = mHash[HashAlg].HashUpdate (HashCtx, HashBase, HashSize); - if (!Status) { - goto Done; - } - } - - Status = mHash[HashAlg].HashFinal (HashCtx, mImageDigest); - -Done: - if (HashCtx != NULL) { - FreePool (HashCtx); - } - - if (SectionHeader != NULL) { - FreePool (SectionHeader); - } - - return Status; -} - -/** - Recognize the Hash algorithm in PE/COFF Authenticode and calculate hash of - Pe/Coff image based on the authenticated image hashing in PE/COFF Specification - 8.0 Appendix A - - @retval EFI_UNSUPPORTED Hash algorithm is not supported. - @retval EFI_SUCCESS Hash successfully. - -**/ -EFI_STATUS -HashPeImageByType ( - VOID - ) -{ - UINT8 Index; - WIN_CERTIFICATE_EFI_PKCS *PkcsCertData; - - PkcsCertData = (WIN_CERTIFICATE_EFI_PKCS *)(mImageBase + mSecDataDir->Offset); - - for (Index = 0; Index < HASHALG_MAX; Index++) { - // - // Check the Hash algorithm in PE/COFF Authenticode. - // According to PKCS#7 Definition: - // SignedData ::= SEQUENCE { - // version Version, - // digestAlgorithms DigestAlgorithmIdentifiers, - // contentInfo ContentInfo, - // .... } - // The DigestAlgorithmIdentifiers can be used to determine the hash algorithm in PE/COFF hashing - // This field has the fixed offset (+32) in final Authenticode ASN.1 data. - // Fixed offset (+32) is calculated based on two bytes of length encoding. - // - if ((*(PkcsCertData->CertData + 1) & TWO_BYTE_ENCODE) != TWO_BYTE_ENCODE) { - // - // Only support two bytes of Long Form of Length Encoding. - // - continue; - } - - // - if (CompareMem (PkcsCertData->CertData + 32, mHash[Index].OidValue, mHash[Index].OidLength) == 0) { - break; - } - } - - if (Index == HASHALG_MAX) { - return EFI_UNSUPPORTED; - } - - // - // HASH PE Image based on Hash algorithm in PE/COFF Authenticode. - // - if (!HashPeImage (Index)) { - return EFI_UNSUPPORTED; - } - - return EFI_SUCCESS; -} - /** Enroll a new signature of executable into Signature Database. @@ -2377,8 +1630,12 @@ EnrollImageSignatureToSigDB ( UINTN SigDBSize; UINT32 Attr; WIN_CERTIFICATE_UEFI_GUID *GuidCertData; + WIN_CERTIFICATE_EFI_PKCS *PkcsCertData; EFI_TIME Time; UINT32 HashAlg; + EFI_GUID CertType; + UINTN ImageDigestSize; + UINT8 ImageDigest[MAX_DIGEST_SIZE]; Data = NULL; GuidCertData = NULL; @@ -2411,24 +1668,22 @@ EnrollImageSignatureToSigDB ( ASSERT (mImageBase != NULL); - Status = LoadPeImage (); + Status = GetImageSecDataDir (mImageBase, mImageSize, (EFI_IMAGE_DATA_DIRECTORY **)&mSecDataDir); if (EFI_ERROR (Status)) { goto ON_EXIT; } if (mSecDataDir->SizeOfCert == 0) { Status = EFI_SECURITY_VIOLATION; - HashAlg = sizeof (mHash) / sizeof (HASH_TABLE); + HashAlg = HASHALG_MAX; while (HashAlg > 0) { HashAlg--; - if ((mHash[HashAlg].GetContextSize == NULL) || (mHash[HashAlg].HashInit == NULL) || (mHash[HashAlg].HashUpdate == NULL) || (mHash[HashAlg].HashFinal == NULL)) { - continue; - } - if (HashPeImage (HashAlg)) { + if (HashPeImage (mImageBase, mImageSize, HashAlg, ImageDigest, &ImageDigestSize, &CertType)) { Status = EFI_SUCCESS; break; } + } if (EFI_ERROR (Status)) { @@ -2448,12 +1703,24 @@ EnrollImageSignatureToSigDB ( goto ON_EXIT; } - if (!HashPeImage (HASHALG_SHA256)) { + if (!HashPeImage (mImageBase, mImageSize, HASHALG_SHA256, ImageDigest, &ImageDigestSize, &CertType)) { Status = EFI_ABORTED; goto ON_EXIT; } } else if (mCertificate->wCertificateType == WIN_CERT_TYPE_PKCS_SIGNED_DATA) { - Status = HashPeImageByType (); + PkcsCertData = (WIN_CERTIFICATE_EFI_PKCS *)mCertificate; + if (PkcsCertData->Hdr.dwLength <= sizeof (PkcsCertData->Hdr)) { + Status = EFI_ABORTED; + goto ON_EXIT; + } + Status = HashPeImageByType ( + mImageBase, + mImageSize, + PkcsCertData->CertData, + PkcsCertData->Hdr.dwLength - sizeof (PkcsCertData->Hdr), + ImageDigest, + &ImageDigestSize, + &CertType); if (EFI_ERROR (Status)) { goto ON_EXIT; } @@ -2468,7 +1735,7 @@ EnrollImageSignatureToSigDB ( // SigDBSize = sizeof (EFI_SIGNATURE_LIST) + sizeof (EFI_SIGNATURE_DATA) - 1 - + (UINT32)mImageDigestSize; + + (UINT32)ImageDigestSize; Data = (UINT8 *)AllocateZeroPool (SigDBSize); if (Data == NULL) { @@ -2482,12 +1749,12 @@ EnrollImageSignatureToSigDB ( SigDBCert = (EFI_SIGNATURE_LIST *)Data; SigDBCert->SignatureListSize = (UINT32)SigDBSize; SigDBCert->SignatureHeaderSize = 0; - SigDBCert->SignatureSize = sizeof (EFI_SIGNATURE_DATA) - 1 + (UINT32)mImageDigestSize; - CopyGuid (&SigDBCert->SignatureType, &mCertType); + SigDBCert->SignatureSize = sizeof (EFI_SIGNATURE_DATA) - 1 + (UINT32)ImageDigestSize; + CopyGuid (&SigDBCert->SignatureType, &CertType); SigDBCertData = (EFI_SIGNATURE_DATA *)((UINT8 *)SigDBCert + sizeof (EFI_SIGNATURE_LIST)); CopyGuid (&SigDBCertData->SignatureOwner, Private->SignatureGUID); - CopyMem (SigDBCertData->SignatureData, mImageDigest, mImageDigestSize); + CopyMem (SigDBCertData->SignatureData, ImageDigest, ImageDigestSize); Attr = EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS; @@ -5438,7 +4705,7 @@ SecureBootCallback ( break; case KEY_VALUE_SAVE_AND_EXIT_DBX: - if (IsX509CertInDbx (Private, EFI_IMAGE_SECURITY_DATABASE1)) { + if (IsX509CertInDbx (Private)) { CreatePopUp ( EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE, &Key, diff --git a/SecurityPkg/VariableAuthenticated/SecureBootConfigDxe/SecureBootConfigImpl.h b/SecurityPkg/VariableAuthenticated/SecureBootConfigDxe/SecureBootConfigImpl.h index 3480a8a108..82576b86c2 100644 --- a/SecurityPkg/VariableAuthenticated/SecureBootConfigDxe/SecureBootConfigImpl.h +++ b/SecurityPkg/VariableAuthenticated/SecureBootConfigDxe/SecureBootConfigImpl.h @@ -67,38 +67,10 @@ extern EFI_IFR_GUID_LABEL *mEndLabel; #define TWO_BYTE_ENCODE 0x82 #define BUFFER_MAX_SIZE 100 -// -// SHA-256 digest size in bytes -// -#define SHA256_DIGEST_SIZE 32 -// -// SHA-384 digest size in bytes -// -#define SHA384_DIGEST_SIZE 48 -// -// SHA-512 digest size in bytes -// -#define SHA512_DIGEST_SIZE 64 - -// -// Set max digest size as SHA512 Output (64 bytes) by far -// -#define MAX_DIGEST_SIZE SHA512_DIGEST_SIZE - #define WIN_CERT_UEFI_RSA2048_SIZE 256 #define WIN_CERT_UEFI_RSA3072_SIZE 384 #define WIN_CERT_UEFI_RSA4096_SIZE 512 -// -// Support hash types -// -#define HASHALG_SHA224 0x00000000 -#define HASHALG_SHA256 0x00000001 -#define HASHALG_SHA384 0x00000002 -#define HASHALG_SHA512 0x00000003 -#define HASHALG_RAW 0x00000004 -#define HASHALG_MAX 0x00000004 - // // Certificate public key minimum size (bytes) // @@ -316,11 +288,6 @@ typedef struct { HASH_FINAL HashFinal; ///< Pointer to Hash Final function } HASH_TABLE; -typedef struct { - WIN_CERTIFICATE Hdr; - UINT8 CertData[1]; -} WIN_CERTIFICATE_EFI_PKCS; - /** This function publish the SecureBoot configuration Form. diff --git a/SecurityPkg/VariableAuthenticated/SecureBootConfigDxe/SecureBootConfigStrings.uni b/SecurityPkg/VariableAuthenticated/SecureBootConfigDxe/SecureBootConfigStrings.uni index 43b4dc4a00..fcb5856d9f 100644 --- a/SecurityPkg/VariableAuthenticated/SecureBootConfigDxe/SecureBootConfigStrings.uni +++ b/SecurityPkg/VariableAuthenticated/SecureBootConfigDxe/SecureBootConfigStrings.uni @@ -50,6 +50,8 @@ SPDX-License-Identifier: BSD-2-Clause-Patent #string STR_DBX_CERTIFICATE_FORMAT_PROMPT #language en-US "Signature Format" #string STR_DBX_CERTIFICATE_FORMAT_HELP #language en-US "X509 DER-Cert enrolled. Select different option to enroll it into DBX." +#string STR_DBX_CERTIFICATE_FORMAT_SHA1 #language en-US "X509 CERT SHA1" +#string STR_DBX_CERTIFICATE_FORMAT_SHA224 #language en-US "X509 CERT SHA224" #string STR_DBX_CERTIFICATE_FORMAT_SHA256 #language en-US "X509 CERT SHA256" #string STR_DBX_CERTIFICATE_FORMAT_SHA384 #language en-US "X509 CERT SHA384" #string STR_DBX_CERTIFICATE_FORMAT_SHA512 #language en-US "X509 CERT SHA512" diff --git a/UefiPayloadPkg/UefiPayloadPkg.dsc b/UefiPayloadPkg/UefiPayloadPkg.dsc index 9ff326a6cb..a0742154d1 100644 --- a/UefiPayloadPkg/UefiPayloadPkg.dsc +++ b/UefiPayloadPkg/UefiPayloadPkg.dsc @@ -340,6 +340,7 @@ SmmRelocationLib|UefiCpuPkg/Library/SmmRelocationLib/SmmRelocationLib.inf HobPrintLib|MdeModulePkg/Library/HobPrintLib/HobPrintLib.inf BuildFdtLib|UefiPayloadPkg/Library/BuildFdtLib/BuildFdtLib.inf + DxeImageVerificationLib|SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.inf [LibraryClasses.common] !if $(BOOTSPLASH_IMAGE) @@ -741,7 +742,7 @@ MdeModulePkg/Universal/SecurityStubDxe/SecurityStubDxe.inf { !if $(SECURE_BOOT_ENABLE) == TRUE - NULL|SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.inf + NULL|SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationHandler.inf !endif } !endif From 9436bd596ce4a8a066107bdc79cbb9c04fb70fbf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20=C5=BBygowski?= Date: Tue, 8 Jul 2025 10:51:28 +0200 Subject: [PATCH 03/28] DMP/SovereignBootWizard: Restore SB defaults when SV Boot is not selected MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Michał Żygowski --- .../SovereignBootWizard/SovereignBootWizard.c | 141 +++++++++++++++++- .../SovereignBootWizard/SovereignBootWizard.h | 1 + 2 files changed, 139 insertions(+), 3 deletions(-) diff --git a/DasharoModulePkg/Application/SovereignBootWizard/SovereignBootWizard.c b/DasharoModulePkg/Application/SovereignBootWizard/SovereignBootWizard.c index d4df2a138f..11dc180af9 100644 --- a/DasharoModulePkg/Application/SovereignBootWizard/SovereignBootWizard.c +++ b/DasharoModulePkg/Application/SovereignBootWizard/SovereignBootWizard.c @@ -210,6 +210,112 @@ RouteConfig ( return EFI_SUCCESS; } +EFI_STATUS +DeleteAllSecureBootVariables ( + VOID + ) +{ + EFI_STATUS Status, TempStatus; + + Status = DeletePlatformKey (); + DEBUG ((DEBUG_INFO, "%a - PK Delete = %r\n", __func__, Status)); + // If the PK is not found, then our work here is done. + if (Status == EFI_NOT_FOUND) { + Status = EFI_SUCCESS; + } + // If any other error occurred, let's inform the caller that the PK delete in particular failed. + else if (EFI_ERROR (Status)) { + Status = EFI_ABORTED; + } + + // + // If any of THESE steps have an error, report the error but attempt to delete all keys. + // Using TempStatus will prevent an error from being trampled by an EFI_SUCCESS. + // Overwrite Status ONLY if TempStatus is an error. + // + // If the error is EFI_NOT_FOUND, we can safely ignore it since we were trying to delete + // the variables anyway. + // + TempStatus = DeleteKEK (); + DEBUG ((DEBUG_INFO, "%a - KEK Delete = %r\n", __func__, TempStatus)); + if (EFI_ERROR (TempStatus) && (TempStatus != EFI_NOT_FOUND)) { + Status = EFI_ACCESS_DENIED; + } + + TempStatus = DeleteDb (); + DEBUG ((DEBUG_INFO, "%a - db Delete = %r\n", __func__, TempStatus)); + if (EFI_ERROR (TempStatus) && (TempStatus != EFI_NOT_FOUND)) { + Status = EFI_ACCESS_DENIED; + } + + TempStatus = DeleteDbx (); + DEBUG ((DEBUG_INFO, "%a - dbx Delete = %r\n", __func__, TempStatus)); + if (EFI_ERROR (TempStatus) && (TempStatus != EFI_NOT_FOUND)) { + Status = EFI_ACCESS_DENIED; + } + + TempStatus = DeleteDbt (); + DEBUG ((DEBUG_INFO, "%a - dbt Delete = %r\n", __func__, TempStatus)); + if (EFI_ERROR (TempStatus) && (TempStatus != EFI_NOT_FOUND)) { + Status = EFI_ACCESS_DENIED; + } + + return Status; +} + +EFI_STATUS +EnrollDefaultSecureBootVariables ( + VOID + ) +{ + EFI_STATUS Status; + + Status = EnrollDbFromDefault (); + if (EFI_ERROR (Status)) { + return Status; + } + + Status = EnrollDbxFromDefault (); + if (EFI_ERROR (Status)) { + return Status; + } + + // Default dbt may not exists and is not critical error if fails + EnrollDbtFromDefault (); + + Status = EnrollKEKFromDefault (); + if (EFI_ERROR (Status)) { + return Status; + } + + return EnrollPKFromDefault (); +} + +EFI_STATUS +RestoreSecureBootDefaults ( + VOID + ) +{ + EFI_STATUS Status; + + Status = SetSecureBootMode (CUSTOM_SECURE_BOOT_MODE); + if (EFI_ERROR (Status)) { + return Status; + } + + Status = DeleteAllSecureBootVariables (); + if (EFI_ERROR (Status)) { + return Status; + } + + Status = EnrollDefaultSecureBootVariables (); + if (EFI_ERROR (Status)) { + return Status; + } + + return SetSecureBootMode (STANDARD_SECURE_BOOT_MODE); +} + /** This function processes the results of changes in configuration. @@ -244,8 +350,10 @@ Callback ( ) { EFI_STATUS Status; - SOVEREIGN_BOOT_WIZARD_PRIVATE_DATA *PrivateData; + SOVEREIGN_BOOT_WIZARD_PRIVATE_DATA *PrivateData; EFI_INPUT_KEY Key; + UINTN BufferSize; + SOVEREIGN_BOOT_WIZARD_NV_CONFIG SvConfig; if (((Value == NULL) && (Action != EFI_BROWSER_ACTION_FORM_OPEN) && (Action != EFI_BROWSER_ACTION_FORM_CLOSE)) || (ActionRequest == NULL)) @@ -263,18 +371,45 @@ Callback ( switch (QuestionId) { case SELECT_DEFAULT_SECURE_BOOT_QUESTION_ID: { - // TODO: // 1. Set the Sovering Boot option to disabled. // 2. Unset the system provisioned state (just in case it was // provisioned before). + BufferSize = sizeof (SOVEREIGN_BOOT_WIZARD_NV_CONFIG); + SetMem(&SvConfig, BufferSize, 0); + Status = gRT->SetVariable ( + mSvBootConfigVarName, + &gSovereignBootWizardFormSetGuid, + EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS, + BufferSize, + &SvConfig + ); + if (EFI_ERROR (Status)) { + return Status; + } // 3. Restore default keys if necessary. Maybe use NV VendorKeys to // indicate if key restoration is required. + Status = RestoreSecureBootDefaults (); + if (EFI_ERROR (Status)) { + return Status; + } // 4. Reset the system to boot in a fresh state. Can't really avoid // the reset as we cannot exit the form in any other action than // EFI_BROWSER_ACTION_CHANGED, but it can't be invoked for // EFI_IFR_TYPE_ACTION nor EFI_IFR_TYPE_REF. Also we should reset - // in case Secure Boto keys get restored to defaults. We can show + // in case Secure Boot keys get restored to defaults. We can show // a pop-up to inform about the actions we make here. + do { + CreatePopUp ( + EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE, + &Key, + L"", + L"Default Secure Boot configuration has been restored.", + L"", + L"Press ENTER to reset the system ...", + L"", + NULL + ); + } while (Key.UnicodeChar != CHAR_CARRIAGE_RETURN); gRT->ResetSystem (EfiResetCold, EFI_SUCCESS, 0, NULL); break; diff --git a/DasharoModulePkg/Application/SovereignBootWizard/SovereignBootWizard.h b/DasharoModulePkg/Application/SovereignBootWizard/SovereignBootWizard.h index 4b69ea7046..ca38ce174f 100644 --- a/DasharoModulePkg/Application/SovereignBootWizard/SovereignBootWizard.h +++ b/DasharoModulePkg/Application/SovereignBootWizard/SovereignBootWizard.h @@ -36,6 +36,7 @@ Revision History #include #include #include +#include #include From 63ed99bc6f7fc580e60c3ccd19f3ab7a015504a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20=C5=BBygowski?= Date: Tue, 8 Jul 2025 11:44:07 +0200 Subject: [PATCH 04/28] DMP/SovereignBootWizard: Prepare SB variables SV Boot provisioning MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Michał Żygowski --- .../SovereignBootWizard/SovereignBootWizard.c | 46 ++++++++++++++++--- .../SovereignBootWizardHii.h | 2 +- 2 files changed, 40 insertions(+), 8 deletions(-) diff --git a/DasharoModulePkg/Application/SovereignBootWizard/SovereignBootWizard.c b/DasharoModulePkg/Application/SovereignBootWizard/SovereignBootWizard.c index 11dc180af9..7b2afea23a 100644 --- a/DasharoModulePkg/Application/SovereignBootWizard/SovereignBootWizard.c +++ b/DasharoModulePkg/Application/SovereignBootWizard/SovereignBootWizard.c @@ -316,6 +316,33 @@ RestoreSecureBootDefaults ( return SetSecureBootMode (STANDARD_SECURE_BOOT_MODE); } +EFI_STATUS +PrepareSbVariablesForSvBoot ( + VOID + ) +{ + EFI_STATUS Status; + + Status = SetSecureBootMode (CUSTOM_SECURE_BOOT_MODE); + if (EFI_ERROR (Status)) { + return Status; + } + + Status = DeleteAllSecureBootVariables (); + if (EFI_ERROR (Status)) { + return Status; + } + + // For Sovereign Boot we want to keep dbx to prohibit booting + // known revoked, buggy and malicious images. + Status = EnrollDbxFromDefault (); + if (EFI_ERROR (Status)) { + return Status; + } + + return SetSecureBootMode (STANDARD_SECURE_BOOT_MODE); +} + /** This function processes the results of changes in configuration. @@ -405,7 +432,7 @@ Callback ( L"", L"Default Secure Boot configuration has been restored.", L"", - L"Press ENTER to reset the system ...", + L"Press ENTER to reset the system...", L"", NULL ); @@ -414,6 +441,14 @@ Callback ( gRT->ResetSystem (EfiResetCold, EFI_SUCCESS, 0, NULL); break; } + case SELECT_SOVEREIGN_BOOT_QUESTION_ID: + { + // When selecting Sovereign Boot, we have to wipe out the SB + // variables except keeping default dbx. Then the wizard will + // proceed with enrolling trusted keys into db. + Status = PrepareSbVariablesForSvBoot (); + break; + } default: break; } @@ -440,7 +475,7 @@ Callback ( L"", L"No more bootloaders found.", L"", - L"Press ENTER to continue ...", + L"Press ENTER to continue...", L"", NULL ); @@ -471,7 +506,7 @@ Callback ( L"", L"Could not get bootloader description.", L"", - L"Press ENTER to continue ...", + L"Press ENTER to continue...", L"", NULL ); @@ -503,9 +538,6 @@ Callback ( { switch (QuestionId) { case DO_NOT_TRUST_KEY_FORM2_QUESTION_ID: - case TRUST_KEY_AND_BOOT_FORM2_QUESTION_ID: - case TRUST_KEY_FORM2_QUESTION_ID: - case SHOW_KEY_DETAILS_FORM2_QUESTION_ID: if (!mBootloadersInitted) { Status = GetBootOptions (PrivateData); DEBUG ((EFI_D_INFO, "GetBootOptions: %r\n", mBootloaderIndex, Status)); @@ -524,7 +556,7 @@ Callback ( L"", L"Could not find any bootloaders.", L"", - L"Press ENTER to continue ...", + L"Press ENTER to continue...", L"", NULL ); diff --git a/DasharoModulePkg/Application/SovereignBootWizard/SovereignBootWizardHii.h b/DasharoModulePkg/Application/SovereignBootWizard/SovereignBootWizardHii.h index f5b82fd765..f77c2cdacd 100644 --- a/DasharoModulePkg/Application/SovereignBootWizard/SovereignBootWizardHii.h +++ b/DasharoModulePkg/Application/SovereignBootWizard/SovereignBootWizardHii.h @@ -36,7 +36,7 @@ Revision History: #define SOVEREIGN_BOOT_WIZARD_FORM_QUESTION_ID_BASE 0x1000 // Welcome form #define WELCOME_FORM_QUESTION_ID_BASE 0x1100 -#define SELECT_SOVEREIGN_BOOT_QUESTION_ID 0x1101 +#define SELECT_SOVEREIGN_BOOT_QUESTION_ID 0x1101 #define SELECT_DEFAULT_SECURE_BOOT_QUESTION_ID 0x1102 // Configuration form From 203f77388ea87d742f92454868fdaee70ac6eccf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20=C5=BBygowski?= Date: Tue, 8 Jul 2025 12:28:45 +0200 Subject: [PATCH 05/28] DMP/Application/SovereignBootWizard; Fix exiting the forms MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit SendForm opens a new formset, but previous formset is saved in the browsing history. When exit is selected, the form browser simply goes back to the previous formset instead of exiting the application. Fix it by setting proper form browsing scope and requesting an exit from current form before opening a new one. Signed-off-by: Michał Żygowski --- .../SovereignBootWizard/SovereignBootWizard.c | 38 +++++++++++++------ .../SovereignBootWizard/SovereignBootWizard.h | 1 + 2 files changed, 28 insertions(+), 11 deletions(-) diff --git a/DasharoModulePkg/Application/SovereignBootWizard/SovereignBootWizard.c b/DasharoModulePkg/Application/SovereignBootWizard/SovereignBootWizard.c index 7b2afea23a..4780bd0116 100644 --- a/DasharoModulePkg/Application/SovereignBootWizard/SovereignBootWizard.c +++ b/DasharoModulePkg/Application/SovereignBootWizard/SovereignBootWizard.c @@ -381,6 +381,7 @@ Callback ( EFI_INPUT_KEY Key; UINTN BufferSize; SOVEREIGN_BOOT_WIZARD_NV_CONFIG SvConfig; + BROWSER_SETTING_SCOPE Scope; if (((Value == NULL) && (Action != EFI_BROWSER_ACTION_FORM_OPEN) && (Action != EFI_BROWSER_ACTION_FORM_CLOSE)) || (ActionRequest == NULL)) @@ -461,6 +462,14 @@ Callback ( case EXIT_FORM2_QUESTION_ID: case EXIT_FORM3_QUESTION_ID: case EXIT_FORM9_QUESTION_ID: + if (PrivateData->ConfigData.AppLaunchCause == SV_BOOT_LAUNCH_VIA_SETUP) { + Scope = FormSetLevel; + } else { + Scope = SystemLevel; + } + PrivateData->FormBrowserEx2->SetScope (Scope); + Status = PrivateData->FormBrowserEx2->ExecuteAction(BROWSER_ACTION_EXIT, 0); + *ActionRequest = EFI_BROWSER_ACTION_REQUEST_EXIT; break; case DO_NOT_TRUST_KEY_FORM2_QUESTION_ID: @@ -483,11 +492,13 @@ Callback ( // TODO: Here // 1. If nothing has been selected as trusted so far, warn popup. // 2. Create ephemeral PK. Enroll it and enable Secure Boot. - // 3. Set the SV boto variable to provisioned state. + // 3. Set the SV boot variable to provisioned state. // 4. Boot the first trusted bootloader. - // No more bootloaders, reset the bootloader index and - // go to ineractive mode form for now. + // No more bootloaders, exit the formset, reset the bootloader + // index and go to ineractive mode form for now. + PrivateData->FormBrowserEx2->SetScope (FormSetLevel); + Status = PrivateData->FormBrowserEx2->ExecuteAction(BROWSER_ACTION_EXIT, 0); mBootloaderIndex = 0; Status = PrivateData->FormBrowser2->SendForm ( PrivateData->FormBrowser2, @@ -605,7 +616,6 @@ SovereignBootWizardInit ( IN EFI_SYSTEM_TABLE *SystemTable ) { - EFI_STATUS FormBrowserStatus; EFI_STATUS Status; EFI_HII_HANDLE HiiHandle; EFI_SCREEN_DESCRIPTOR Screen; @@ -682,6 +692,13 @@ SovereignBootWizardInit ( mPrivateData->FormBrowser2 = FormBrowser2; + Status = gBS->LocateProtocol (&gEdkiiFormBrowserEx2ProtocolGuid, NULL, (VOID **)&FormBrowserEx2); + if (EFI_ERROR (Status)) { + ASSERT_EFI_ERROR (Status); + return Status; + } + mPrivateData->FormBrowserEx2 = FormBrowserEx2; + // // Locate ConfigRouting protocol // @@ -839,13 +856,12 @@ SovereignBootWizardInit ( // // Override Hotkeys, F9 and F10 won't be needed by this application // - FormBrowserStatus = gBS->LocateProtocol (&gEdkiiFormBrowserEx2ProtocolGuid, NULL, (VOID **)&FormBrowserEx2); - if (!EFI_ERROR (FormBrowserStatus)) { + if (mPrivateData->FormBrowserEx2 != NULL) { HotKey.UnicodeChar = CHAR_NULL; HotKey.ScanCode = SCAN_F9; - FormBrowserEx2->RegisterHotKey (&HotKey, 0, 0, NULL); + mPrivateData->FormBrowserEx2->RegisterHotKey (&HotKey, 0, 0, NULL); HotKey.ScanCode = SCAN_F10; - FormBrowserEx2->RegisterHotKey (&HotKey, 0, 0, NULL); + mPrivateData->FormBrowserEx2->RegisterHotKey (&HotKey, 0, 0, NULL); } if (SvConfig->SvBootProvisioned) { @@ -876,7 +892,7 @@ SovereignBootWizardInit ( ASSERT_EFI_ERROR (Status); - if (!EFI_ERROR (FormBrowserStatus)) { + if (mPrivateData->FormBrowserEx2 != NULL) { // // Register the default HotKey F9 and F10 again. // @@ -884,13 +900,13 @@ SovereignBootWizardInit ( HotKey.ScanCode = SCAN_F10; NewString = HiiGetString (HiiHandle, STRING_TOKEN (FUNCTION_TEN_STRING), NULL); ASSERT (NewString != NULL); - FormBrowserEx2->RegisterHotKey (&HotKey, BROWSER_ACTION_SUBMIT, 0, NewString); + mPrivateData->FormBrowserEx2->RegisterHotKey (&HotKey, BROWSER_ACTION_SUBMIT, 0, NewString); FreePool (NewString); HotKey.ScanCode = SCAN_F9; NewString = HiiGetString (HiiHandle, STRING_TOKEN (FUNCTION_NINE_STRING), NULL); ASSERT (NewString != NULL); - FormBrowserEx2->RegisterHotKey (&HotKey, BROWSER_ACTION_DEFAULT, EFI_HII_DEFAULT_CLASS_STANDARD, NewString); + mPrivateData->FormBrowserEx2->RegisterHotKey (&HotKey, BROWSER_ACTION_DEFAULT, EFI_HII_DEFAULT_CLASS_STANDARD, NewString); FreePool (NewString); } diff --git a/DasharoModulePkg/Application/SovereignBootWizard/SovereignBootWizard.h b/DasharoModulePkg/Application/SovereignBootWizard/SovereignBootWizard.h index ca38ce174f..21a97a1561 100644 --- a/DasharoModulePkg/Application/SovereignBootWizard/SovereignBootWizard.h +++ b/DasharoModulePkg/Application/SovereignBootWizard/SovereignBootWizard.h @@ -95,6 +95,7 @@ typedef struct { EFI_HII_POPUP_PROTOCOL *HiiPopup; EFI_FORM_BROWSER2_PROTOCOL *FormBrowser2; + EDKII_FORM_BROWSER_EXTENSION2_PROTOCOL *FormBrowserEx2; EFI_HII_CONFIG_ACCESS_PROTOCOL ConfigAccess; From 0cb92fe6e381e32d65e8cc4aaf1b31fd8f9db4eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20=C5=BBygowski?= Date: Mon, 21 Jul 2025 16:26:12 +0200 Subject: [PATCH 06/28] DMP/Application/SovereignBootWizard: Parse image signatures MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Michał Żygowski --- .../SovereignBootWizard/BootOptionParsing.c | 46 +- .../SovereignBootWizard/SignatureParsing.c | 557 ++++++++++++++++++ .../SovereignBootWizard/SovereignBootWizard.c | 16 +- .../SovereignBootWizard/SovereignBootWizard.h | 75 ++- .../SovereignBootWizard.inf | 4 + .../SovereignBootWizardHii.h | 2 +- .../SovereignBootWizardVfr.vfr | 3 + .../SovereignBootWizardVfrStrings.uni | 3 +- 8 files changed, 672 insertions(+), 34 deletions(-) create mode 100644 DasharoModulePkg/Application/SovereignBootWizard/SignatureParsing.c diff --git a/DasharoModulePkg/Application/SovereignBootWizard/BootOptionParsing.c b/DasharoModulePkg/Application/SovereignBootWizard/BootOptionParsing.c index d004536821..38ebcab450 100644 --- a/DasharoModulePkg/Application/SovereignBootWizard/BootOptionParsing.c +++ b/DasharoModulePkg/Application/SovereignBootWizard/BootOptionParsing.c @@ -310,8 +310,8 @@ GetBootOptions ( LoadOptionPtr += sizeof (UINT32); - NewLoadContext->FilePathListLength = *(UINT16 *)LoadOptionPtr; - LoadOptionPtr += sizeof (UINT16); + NewLoadContext->FilePathLength = *(UINT16 *)LoadOptionPtr; + LoadOptionPtr += sizeof (UINT16); StringSize = StrSize (L"Description: ") + StrSize ((UINT16 *)LoadOptionPtr) + sizeof(CHAR16); NewLoadContext->Description = AllocateZeroPool (StringSize); @@ -323,20 +323,20 @@ GetBootOptions ( LoadOptionPtr += StrSize ((UINT16 *)LoadOptionPtr); - NewLoadContext->FilePathList = AllocateZeroPool (NewLoadContext->FilePathListLength); - ASSERT (NewLoadContext->FilePathList != NULL); + NewLoadContext->FilePath = AllocateZeroPool (NewLoadContext->FilePathLength); + ASSERT (NewLoadContext->FilePath != NULL); CopyMem ( - NewLoadContext->FilePathList, + NewLoadContext->FilePath, (EFI_DEVICE_PATH_PROTOCOL *)LoadOptionPtr, - NewLoadContext->FilePathListLength + NewLoadContext->FilePathLength ); // Hardware Path to the disk - HwDevicePath = StripFilePath (NewLoadContext->FilePathList); + HwDevicePath = StripFilePath (NewLoadContext->FilePath); if (HwDevicePath == NULL) { // In case there is no file device path, it means it is /EFI/BOOT/BOOTX64.efi // and is automatically expanded by UEFI boot manager - PathString = UiDevicePathToStr (Private->DevPathToText, NewLoadContext->FilePathList); + PathString = UiDevicePathToStr (Private->DevPathToText, NewLoadContext->FilePath); } else { PathString = UiDevicePathToStr (Private->DevPathToText, HwDevicePath); FreePool (HwDevicePath); @@ -351,12 +351,13 @@ GetBootOptions ( // File path on the disk if (HwDevicePath != NULL) { - PathString = UiDevicePathToStr (Private->DevPathToText, ExtractFilePath (NewLoadContext->FilePathList)); + PathString = UiDevicePathToStr (Private->DevPathToText, ExtractFilePath (NewLoadContext->FilePath)); ASSERT (PathString != NULL); } else { // In case there is no file device path, it means it is /EFI/BOOT/BOOTX64.efi // and is automatically expanded by UEFI boot manager PathString = EFI_REMOVABLE_MEDIA_FILE_NAME; + NewLoadContext->NeedsPathExpansion = TRUE; } StringSize = StrSize (L"File path: ") + StrSize (PathString) + sizeof(CHAR16); NewMenuEntry->FilePathString = AllocateZeroPool (StringSize); @@ -367,6 +368,8 @@ GetBootOptions ( } NewMenuEntry->FilePathStringToken = HiiSetString (Private->HiiHandle, 0, NewMenuEntry->FilePathString, NULL); + FillSecurityContext(NewMenuEntry); + InsertTailList (&BootOptionMenu.Head, &NewMenuEntry->Link); MenuCount++; FreePool (LoadOptionFromVar); @@ -422,15 +425,15 @@ GetMenuEntry ( EFI_STATUS UpdateBootloaderPage ( - IN SOVEREIGN_BOOT_WIZARD_PRIVATE_DATA *Private, - IN UINTN OptionNumber + IN SOVEREIGN_BOOT_WIZARD_PRIVATE_DATA *Private ) { BM_MENU_ENTRY *BootloaderEntry; BM_LOAD_CONTEXT *BootloaderContext; EFI_STRING NewString; + EFI_STATUS Status; - BootloaderEntry = GetMenuEntry (&BootOptionMenu, OptionNumber); + BootloaderEntry = GetMenuEntry (&BootOptionMenu, mBootloaderIndex); if (BootloaderEntry == NULL) { return EFI_NO_MEDIA; } @@ -445,24 +448,33 @@ UpdateBootloaderPage ( if (NewString != NULL) { HiiSetString (Private->HiiHandle, STRING_TOKEN (STR_BOOTOPT_DESCRIPTION), NewString, NULL); } else { - return EFI_NOT_FOUND; + HiiSetString (Private->HiiHandle, STRING_TOKEN (STR_BOOTOPT_DESCRIPTION), L"Not Found!", NULL); } NewString = HiiGetString (Private->HiiHandle, BootloaderEntry->DevicePathStringToken, NULL); if (NewString != NULL) { HiiSetString (Private->HiiHandle, STRING_TOKEN (STR_HW_PATH), NewString, NULL); } else { - return EFI_NOT_FOUND; + HiiSetString (Private->HiiHandle, STRING_TOKEN (STR_BOOTOPT_DESCRIPTION), L"Not Found!", NULL); } NewString = HiiGetString (Private->HiiHandle, BootloaderEntry->FilePathStringToken, NULL); if (NewString != NULL) { HiiSetString (Private->HiiHandle, STRING_TOKEN (STR_FILE_PATH), NewString, NULL); } else { - return EFI_NOT_FOUND; + HiiSetString (Private->HiiHandle, STRING_TOKEN (STR_BOOTOPT_DESCRIPTION), L"Not Found!", NULL); } - // TODO fingerprint and key details + Status = UpdateCertInfo (Private, mBootloaderIndex, mCertIndex); + mCertIndex++; + if (Status == EFI_NO_MEDIA || ((mCertIndex > 1) && Private->FormData.ImageUnsigned)) { + DEBUG ((EFI_D_INFO, "No more keys/certs for bootloader %u (current certificate %u)\n", + mBootloaderIndex, mCertIndex - 1)); + // No more keys/certs to show for this bootloader, proceed to the next one + mCertIndex = 0; + mBootloaderIndex++; + Status = UpdateBootloaderPage(Private); + } - return EFI_SUCCESS; + return Status; } diff --git a/DasharoModulePkg/Application/SovereignBootWizard/SignatureParsing.c b/DasharoModulePkg/Application/SovereignBootWizard/SignatureParsing.c new file mode 100644 index 0000000000..66460e08aa --- /dev/null +++ b/DasharoModulePkg/Application/SovereignBootWizard/SignatureParsing.c @@ -0,0 +1,557 @@ +/** @file +Sovereign Boot Wizard signature parsing. + +Copyright (c) 2025, 3mdeb Sp z o.o. All rights reserved.
+SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "SovereignBootWizard.h" + +#define ALIGNMENT_SIZE 8 +#define ALIGN_SIZE(a) (((a) % ALIGNMENT_SIZE) ? ALIGNMENT_SIZE - ((a) % ALIGNMENT_SIZE) : 0) + +/** + Read file content into BufferPtr, the size of the allocate buffer + is *FileSize plus AdditionAllocateSize. + + @param[in] FileHandle The file to be read. + @param[in, out] BufferPtr Pointers to the pointer of allocated buffer. + @param[out] FileSize Size of input file + @param[out] AuthStatus File authentication status. + + @retval EFI_SUCCESS The file was read into the buffer. + @retval EFI_INVALID_PARAMETER A parameter was invalid. + @retval EFI_NOT_FOUND The file was not found. +**/ +EFI_STATUS +ReadFileContent ( + IN EFI_DEVICE_PATH_PROTOCOL *FilePath, + IN OUT VOID **BufferPtr, + OUT UINTN *FileSize, + OUT UINT32 *AuthStatus + ) + +{ + if ((FilePath == NULL) || (FileSize == NULL) || (BufferPtr == NULL)) { + return EFI_INVALID_PARAMETER; + } + + *BufferPtr = GetFileBufferByFilePath (TRUE, FilePath, FileSize, AuthStatus); + + if (*BufferPtr == NULL || *FileSize == 0) { + return EFI_NOT_FOUND; + } + + return EFI_SUCCESS; +} + +VOID +VerifyImageHashInDatabases ( + IN VOID *FileBuffer, + IN UINTN FileSize, + IN BM_SECURITY_CONTEXT *SecCtx + ) +{ + EFI_STATUS DbStatus; + BOOLEAN IsFound; + UINT8 HashAlg; + UINT8 ImageDigest[MAX_DIGEST_SIZE]; + UINTN ImageDigestSize; + EFI_GUID CertType; + + HashAlg = HASHALG_MAX; + while (HashAlg > 0) { + HashAlg--; + + if (!HashPeImage (FileBuffer, FileSize, HashAlg, ImageDigest, &ImageDigestSize, &CertType)) { + continue; + } + + DbStatus = IsSignatureFoundInDatabase ( + EFI_IMAGE_SECURITY_DATABASE1, + ImageDigest, + &CertType, + ImageDigestSize, + &IsFound + ); + if (!EFI_ERROR (DbStatus) || IsFound) { + SecCtx->ImageIsInDb = TRUE; + } + + DbStatus = IsSignatureFoundInDatabase ( + EFI_IMAGE_SECURITY_DATABASE, + ImageDigest, + &CertType, + ImageDigestSize, + &IsFound + ); + if (!EFI_ERROR (DbStatus) && IsFound) { + SecCtx->ImageIsInDbx = TRUE; + } + } +} + +VOID +FillCertificateEntries ( + IN VOID *FileBuffer, + IN UINTN FileSize, + IN OUT BM_SECURITY_CONTEXT *SecCtx, + IN EFI_IMAGE_DATA_DIRECTORY *SecDataDir + ) +{ + UINT32 SecDataDirEnd; + UINT32 SecDataDirLeft; + WIN_CERTIFICATE *WinCertificate; + WIN_CERTIFICATE_EFI_PKCS *PkcsCertData; + WIN_CERTIFICATE_UEFI_GUID *WinCertUefiGuid; + UINT32 Offset; + UINT8 *AuthData; + UINTN AuthDataSize; + EFI_STATUS HashStatus; + UINT8 ImageDigest[MAX_DIGEST_SIZE]; + UINTN ImageDigestSize; + BM_CERT_ENTRY *NewCertEntry; + UINT8 *CertBuffer; + UINTN BufferLength; + UINT8 *TrustedCert; + UINTN TrustedCertLength; + UINTN CertCount; + + CertCount = 0; + // + // Verify the signature of the image, multiple signatures are allowed as per PE/COFF Section 4.7 + // "Attribute Certificate Table". + // The first certificate starts at offset (SecDataDir->VirtualAddress) from the start of the file. + // + SecDataDirEnd = SecDataDir->VirtualAddress + SecDataDir->Size; + for (Offset = SecDataDir->VirtualAddress; + Offset < SecDataDirEnd; + Offset += (WinCertificate->dwLength + ALIGN_SIZE (WinCertificate->dwLength))) + { + SecDataDirLeft = SecDataDirEnd - Offset; + if (SecDataDirLeft <= sizeof (WIN_CERTIFICATE)) { + DEBUG ((EFI_D_INFO, "Image Security Data Size too small\n")); + break; + } + + WinCertificate = (WIN_CERTIFICATE *)(FileBuffer + Offset); + if ((SecDataDirLeft < WinCertificate->dwLength) || + (SecDataDirLeft - WinCertificate->dwLength < + ALIGN_SIZE (WinCertificate->dwLength))) + { + DEBUG ((EFI_D_INFO, "Image Security Data Size too small\n")); + break; + } + + // + // Verify the image's Authenticode signature, only DER-encoded PKCS#7 signed data is supported. + // + if (WinCertificate->wCertificateType == WIN_CERT_TYPE_PKCS_SIGNED_DATA) { + // + // The certificate is formatted as WIN_CERTIFICATE_EFI_PKCS which is described in the + // Authenticode specification. + // + PkcsCertData = (WIN_CERTIFICATE_EFI_PKCS *)WinCertificate; + if (PkcsCertData->Hdr.dwLength <= sizeof (PkcsCertData->Hdr)) { + DEBUG ((EFI_D_INFO, "PKCS Cert Data too small\n")); + break; + } + + AuthData = PkcsCertData->CertData; + AuthDataSize = PkcsCertData->Hdr.dwLength - sizeof (PkcsCertData->Hdr); + } else if (WinCertificate->wCertificateType == WIN_CERT_TYPE_EFI_GUID) { + // + // The certificate is formatted as WIN_CERTIFICATE_UEFI_GUID which is described in UEFI Spec. + // + WinCertUefiGuid = (WIN_CERTIFICATE_UEFI_GUID *)WinCertificate; + if (WinCertUefiGuid->Hdr.dwLength <= OFFSET_OF (WIN_CERTIFICATE_UEFI_GUID, CertData)) { + DEBUG ((EFI_D_INFO, "WIN Cert UEFI Data too small\n")); + break; + } + + if (!CompareGuid (&WinCertUefiGuid->CertType, &gEfiCertPkcs7Guid)) { + DEBUG ((EFI_D_INFO, "Cert Type not PKCS7\n")); + continue; + } + + AuthData = WinCertUefiGuid->CertData; + AuthDataSize = WinCertUefiGuid->Hdr.dwLength - OFFSET_OF (WIN_CERTIFICATE_UEFI_GUID, CertData); + } else { + if (WinCertificate->dwLength < sizeof (WIN_CERTIFICATE)) { + DEBUG ((EFI_D_INFO, "Unknown Cert Data too small\n")); + break; + } + + DEBUG ((EFI_D_INFO, "Unknown Cert Data, skipping\n")); + continue; + } + + NewCertEntry = (BM_CERT_ENTRY *) AllocateZeroPool (sizeof(BM_CERT_ENTRY)); + + if (NewCertEntry == NULL) { + DEBUG ((EFI_D_ERROR, "Not enough free memory for certificate data\n")); + return; + } + + NewCertEntry->Signature = SOVEREIGN_BOOT_CERT_ENTRY_SIGNATURE; + + // Obtain the signer's certificate + if (!Pkcs7GetSigners (AuthData, AuthDataSize, + &CertBuffer, &BufferLength, + &TrustedCert, &TrustedCertLength)) { + FreePool (NewCertEntry); + DEBUG ((EFI_D_INFO, "Could not get PKCS7 signers\n")); + continue; + } + if ((BufferLength == 0) || (CertBuffer == NULL) || ((*CertBuffer) == 0)) { + FreePool (NewCertEntry); + DEBUG ((EFI_D_INFO, "PKCS7 signers data invalid\n")); + continue; + } + + // Save the buffer to the signer's certificate + NewCertEntry->CertData = TrustedCert; + NewCertEntry->CertDataSize = TrustedCertLength; + + // Free unused cert chain + Pkcs7FreeSigners (CertBuffer); + + NewCertEntry->CertDigest = (UINT8 *)AllocateZeroPool(SHA256_DIGEST_SIZE); + if (NewCertEntry->CertDigest != NULL) { + CalculateCertHash( + NewCertEntry->CertData, + NewCertEntry->CertDataSize, + HASHALG_SHA256, + NewCertEntry->CertDigest); + NewCertEntry->CertDigestSize = SHA256_DIGEST_SIZE; + } + + HashStatus = HashPeImageByType ( + FileBuffer, + FileSize, + AuthData, + AuthDataSize, + ImageDigest, + &ImageDigestSize, + &NewCertEntry->CertType); + if (EFI_ERROR (HashStatus)) { + if (TrustedCert != NULL) { + Pkcs7FreeSigners (TrustedCert); + } + FreePool (NewCertEntry->CertDigest); + FreePool (NewCertEntry); + DEBUG ((EFI_D_ERROR, "Failed to calculate image hash\n")); + continue; + } + + // + // Check the digital signature against the valid certificate in allowed database (db). + if (IsAllowedByDb (AuthData, AuthDataSize, ImageDigest, ImageDigestSize)) { + NewCertEntry->ImageIsVerified = TRUE; + NewCertEntry->CertIsInDb = TRUE; + } + + // + // Check the digital signature against the revoked certificate in forbidden database (dbx). + // + if (IsForbiddenByDbx (AuthData, AuthDataSize, ImageDigest, ImageDigestSize)) { + NewCertEntry->ImageIsVerified = FALSE; + NewCertEntry->CertIsInDbx = TRUE; + } + + DEBUG ((EFI_D_INFO, "Certificate Details:\n" + "\tImageIsVerified: %u\n" + "\tCertIsInDb: %u\n" + "\tCertIsInDbx: %u\n", + NewCertEntry->ImageIsVerified, + NewCertEntry->CertIsInDb, + NewCertEntry->CertIsInDbx)); + + InsertTailList (&SecCtx->Certs, &NewCertEntry->CertLink); + CertCount++; + } + + if (Offset != SecDataDirEnd) { + DEBUG ((EFI_D_ERROR, "The Size in Certificate Table or the attribute certificate table is corrupted.\n")); + } + + SecCtx->NumCertificates = CertCount; +} + +/** + Fill the security information from the image. + + @param Entry The boot option entry data. + + @return EFI_NOT_FOUND Fail to find file. + @return EFI_OUT_OF_RESOURCES Out of memory. + @return EFI_SUCESS Success to fill security context. + +**/ +EFI_STATUS +FillSecurityContext ( + IN BM_MENU_ENTRY *Entry + ) +{ + BM_SECURITY_CONTEXT *SecCtx; + BM_LOAD_CONTEXT *LoadCtx; + EFI_IMAGE_DATA_DIRECTORY *SecDir; + EFI_DEVICE_PATH_PROTOCOL *FullFilePath; + VOID *ImageBase; + UINTN ImageSize; + UINT32 AuthStatus; + EFI_STATUS Status; + EFI_GUID CertType; + + FullFilePath = NULL; + LoadCtx = (BM_LOAD_CONTEXT *)Entry->VariableContext; + SecCtx = (BM_SECURITY_CONTEXT *)AllocateZeroPool(sizeof(BM_SECURITY_CONTEXT)); + Entry->SecurityContext = SecCtx; + + DEBUG ((EFI_D_INFO, "%a: Processing %s\n", __FUNCTION__, LoadCtx->Description)); + + if (SecCtx == NULL) { + DEBUG ((EFI_D_INFO, "Not enough memory for security context\n")); + return EFI_OUT_OF_RESOURCES; + } + + // Expand the Device PAth to the file if needed + if (LoadCtx->NeedsPathExpansion) { + FullFilePath = EfiBootManagerGetNextLoadOptionDevicePath (LoadCtx->FilePath, FullFilePath); + DEBUG ((EFI_D_INFO, "Expanded file path %s\n", + ConvertDevicePathToText(FullFilePath, FALSE, TRUE))); + } else { + FullFilePath = LoadCtx->FilePath; + } + + // + // Read the whole file content + // + Status = ReadFileContent ( + FullFilePath, + (VOID **)&ImageBase, + &ImageSize, + &AuthStatus + ); + + if (LoadCtx->NeedsPathExpansion && (FullFilePath != NULL)) { + FreePool (FullFilePath); + } + + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_INFO, "Failed to read file: %r\n", Status)); + goto ON_EXIT; + } + + Status = GetImageSecDataDir (ImageBase, ImageSize, &SecDir); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_INFO, "Failed to get Image Security Data\n")); + goto ON_EXIT; + } + + // Always calculate SHA256 hash of the image and store it for display and + // possible trust/distrust choice. + SecCtx->ImageDigest = (UINT8 *)AllocateZeroPool(SHA256_DIGEST_SIZE); + HashPeImage (ImageBase, ImageSize, HASHALG_SHA256, SecCtx->ImageDigest, &SecCtx->ImageDigestSize, &CertType); + + // + // Start Image Validation. + // + VerifyImageHashInDatabases (ImageBase, ImageSize, SecCtx); + + // No certificates, end here + if ((SecDir == NULL) || (SecDir->Size == 0)) { + DEBUG ((EFI_D_INFO, "No Image Security Data, image is unsigned\n")); + SecCtx->ImageIsSigned = FALSE; + SecCtx->NumCertificates = 0; + goto ON_EXIT; + } + + SecCtx->ImageIsSigned = TRUE; + + // Parse certificates + InitializeListHead (&SecCtx->Certs); + FillCertificateEntries (ImageBase, ImageSize, SecCtx, SecDir); + Status = EFI_SUCCESS; + + DEBUG ((EFI_D_INFO, "Image Details:\n" + "\tImageIsInDbx: %u\n" + "\tImageIsInDb: %u\n" + "\tImageIsSigned: %u\n" + "\tAuthenticationStatus: %u\n" + "\tNumCertificates: %u\n", + SecCtx->ImageIsInDbx, + SecCtx->ImageIsInDb, + SecCtx->ImageIsSigned, + SecCtx->AuthenticationStatus, + SecCtx->NumCertificates)); + +ON_EXIT: + + if (ImageBase != NULL) { + FreePool (ImageBase); + } + + return Status; +} + +/** + Get the Cert Entry from the list in Menu Entry. + + If CertNumber is great or equal to the number of Cert + Entry in the list, then ASSERT. + + @param MenuOption The Menu Entry to read the cert entry. + @param MenuNumber The index of Cert Entry. + + @return The Menu Entry. + +**/ +BM_CERT_ENTRY * +GetCertEntry ( + BM_MENU_ENTRY *MenuEntry, + UINTN CertNumber + ) +{ + BM_CERT_ENTRY *NewCertEntry; + BM_SECURITY_CONTEXT *SecCtx; + UINTN Index; + LIST_ENTRY *List; + + SecCtx = (BM_SECURITY_CONTEXT *) MenuEntry->SecurityContext; + + if (SecCtx == NULL) { + return NULL; + } + + if (CertNumber >= SecCtx->NumCertificates) { + return NULL; + } + + List = SecCtx->Certs.ForwardLink; + for (Index = 0; Index < CertNumber; Index++) { + List = List->ForwardLink; + } + + NewCertEntry = CR (List, BM_CERT_ENTRY, CertLink, SOVEREIGN_BOOT_CERT_ENTRY_SIGNATURE); + + return NewCertEntry; +} + +/** + Parse hash value from EFI_SIGNATURE_DATA, and save in the CHAR16 type array. + The buffer is callee allocated and should be freed by the caller. + + @param[in] Digest The pointer to the hash value. + @param[in] DigestSize The size of the hash. + @param[out] BufferToReturn Buffer to save the hash value. + + @retval EFI_INVALID_PARAMETER Invalid Hash or Buffer. + @retval EFI_OUT_OF_RESOURCES A memory allocation failed. + @retval EFI_SUCCESS Operation success. +**/ +EFI_STATUS +ParseHashValue ( + IN UINT8 *Digest, + IN UINTN DigestSize, + OUT CHAR16 **BufferToReturn + ) +{ + UINTN Index; + UINTN BufferIndex; + UINTN TotalSize; + + if ((Digest == NULL) || (DigestSize == 0) || (BufferToReturn == NULL)) { + return EFI_INVALID_PARAMETER; + } + + // + // Each byte will split two Hex-number. + // + TotalSize = (DigestSize * 2 * sizeof (CHAR16)); + + *BufferToReturn = AllocateZeroPool (TotalSize); + if (*BufferToReturn == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + for (Index = 0, BufferIndex = 0; Index < DigestSize; Index = Index + 1) { + BufferIndex += UnicodeSPrint (&(*BufferToReturn)[BufferIndex], TotalSize - sizeof (CHAR16) * BufferIndex, L"%02x", Digest[Index]); + } + + return EFI_SUCCESS; +} + +EFI_STATUS +UpdateCertInfo ( + IN SOVEREIGN_BOOT_WIZARD_PRIVATE_DATA *Private, + IN UINTN OptionNumber, + IN UINTN CertNumber + ) +{ + BM_MENU_ENTRY *BootloaderEntry; + BM_CERT_ENTRY *CertificateEntry; + BM_SECURITY_CONTEXT *SecurityContext; + EFI_STRING NewString; + EFI_STRING OldString; + EFI_STATUS Status; + + BootloaderEntry = GetMenuEntry (&BootOptionMenu, OptionNumber); + if (BootloaderEntry == NULL) { + return EFI_NO_MEDIA; + } + + SecurityContext = (BM_SECURITY_CONTEXT *)BootloaderEntry->SecurityContext; + if (SecurityContext == NULL) { + return EFI_NO_MEDIA; + } + + Private->FormData.ImageUnsigned = (!SecurityContext->ImageIsSigned || + (SecurityContext->NumCertificates == 0)); + + OldString = HiiGetString (Private->HiiHandle, STRING_TOKEN (STR_KEY_FINGERPRINT_HASH), NULL); + + // Image is unsigned? Show its hash instead of certificates + if (Private->FormData.ImageUnsigned) { + HiiSetString (Private->HiiHandle, STRING_TOKEN (STR_KEY_FINGERPRINT), L"Image is unsigned !!!\nImage hash (SHA-256):", NULL); + + Status = ParseHashValue (SecurityContext->ImageDigest, SecurityContext->ImageDigestSize, &NewString); + if (!EFI_ERROR (Status) && (NewString != NULL)) { + HiiSetString (Private->HiiHandle, STRING_TOKEN (STR_KEY_FINGERPRINT_HASH), NewString, NULL); + } else { + HiiSetString (Private->HiiHandle, STRING_TOKEN (STR_KEY_FINGERPRINT_HASH), L"Image hash could not be obtained.", NULL); + } + + // Free previous string if any + if (OldString != NULL) { + FreePool (OldString); + } + + return EFI_SUCCESS; + } + + // Image is signed, show its certificate(s) + CertificateEntry = GetCertEntry(BootloaderEntry, CertNumber); + if (CertificateEntry == NULL) { + return EFI_NO_MEDIA; + } + + HiiSetString (Private->HiiHandle, STRING_TOKEN (STR_KEY_FINGERPRINT), L"Certificate fingerprint (SHA-256):", NULL); + + Status = ParseHashValue (CertificateEntry->CertDigest, CertificateEntry->CertDigestSize, &NewString); + if (!EFI_ERROR (Status) && (NewString != NULL)) { + HiiSetString (Private->HiiHandle, STRING_TOKEN (STR_KEY_FINGERPRINT_HASH), NewString, NULL); + } else { + HiiSetString (Private->HiiHandle, STRING_TOKEN (STR_KEY_FINGERPRINT_HASH), L"Could not obtain certificate fingerprint", NULL); + } + + // Free previous string if any + if (OldString != NULL) { + FreePool (OldString); + } + + // TODO key details + + return EFI_SUCCESS; +} diff --git a/DasharoModulePkg/Application/SovereignBootWizard/SovereignBootWizard.c b/DasharoModulePkg/Application/SovereignBootWizard/SovereignBootWizard.c index 4780bd0116..62f2da523b 100644 --- a/DasharoModulePkg/Application/SovereignBootWizard/SovereignBootWizard.c +++ b/DasharoModulePkg/Application/SovereignBootWizard/SovereignBootWizard.c @@ -39,7 +39,8 @@ STATIC HII_VENDOR_DEVICE_PATH mHiiVendorDevicePath = { } }; -STATIC UINTN mBootloaderIndex = 0; +UINTN mBootloaderIndex = 0; +UINTN mCertIndex = 0; EFI_STATUS EFIAPI @@ -392,8 +393,6 @@ Callback ( Status = EFI_SUCCESS; PrivateData = SOVEREIGN_BOOT_WIZARD_PRIVATE_FROM_THIS (This); - DEBUG ((EFI_D_INFO, "Callback: Action %x QuestionID %x Type %x\n", Action, QuestionId, Type)); - switch (Action) { case EFI_BROWSER_ACTION_CHANGING: switch (QuestionId) { @@ -475,7 +474,7 @@ Callback ( case DO_NOT_TRUST_KEY_FORM2_QUESTION_ID: case TRUST_KEY_FORM2_QUESTION_ID: if (mBootloadersInitted) { - Status = UpdateBootloaderPage (PrivateData, ++mBootloaderIndex); + Status = UpdateBootloaderPage (PrivateData); if (Status == EFI_NO_MEDIA) { do { CreatePopUp ( @@ -555,7 +554,7 @@ Callback ( if (!EFI_ERROR (Status)) { mBootloadersInitted = TRUE; if (!mBootloadersShown) { - Status = UpdateBootloaderPage (PrivateData, mBootloaderIndex); + Status = UpdateBootloaderPage (PrivateData); DEBUG ((EFI_D_INFO, "UpdateBootloaderPage(%d): %r\n", mBootloaderIndex, Status)); mBootloadersShown = !EFI_ERROR (Status); } @@ -597,6 +596,13 @@ Callback ( break; } + HiiSetBrowserData ( + &gSovereignBootWizardFormSetGuid, + mVarStoreName, + sizeof (SOVEREIGN_BOOT_WIZARD_FORM_DATA), + (CONST UINT8 *)&PrivateData->FormData, + NULL); + return Status; } diff --git a/DasharoModulePkg/Application/SovereignBootWizard/SovereignBootWizard.h b/DasharoModulePkg/Application/SovereignBootWizard/SovereignBootWizard.h index 21a97a1561..5c3c03ddc8 100644 --- a/DasharoModulePkg/Application/SovereignBootWizard/SovereignBootWizard.h +++ b/DasharoModulePkg/Application/SovereignBootWizard/SovereignBootWizard.h @@ -20,6 +20,7 @@ Revision History #include +#include #include #include @@ -43,6 +44,8 @@ Revision History #include #include #include +#include +#include #include #include #include @@ -56,7 +59,7 @@ Revision History #include #include #include - +#include #include "SovereignBootWizardHii.h" @@ -70,8 +73,8 @@ extern UINT8 SovereignBootWizardStrings[]; #define SOVEREIGN_BOOT_PRIVATE_SIGNATURE SIGNATURE_32 ('S', 'B', 'p', 's') #define SOVEREIGN_BOOT_MENU_OPTION_SIGNATURE SIGNATURE_32 ('m', 'e', 'n', 'u') -#define SOVEREIGN_BOOT_LOAD_OPTION_SIGNATURE SIGNATURE_32 ('l', 'o', 'a', 'd') #define SOVEREIGN_BOOT_MENU_ENTRY_SIGNATURE SIGNATURE_32 ('e', 'n', 't', 'r') +#define SOVEREIGN_BOOT_CERT_ENTRY_SIGNATURE SIGNATURE_32 ('c', 'e', 'r', 't') #define SOVEREIGN_BOOT_LOAD_CONTEXT_SELECT 0x0 #define SOVEREIGN_BOOT_FILE_CONTEXT_SELECT 0x2 @@ -81,9 +84,9 @@ typedef struct { EFI_HANDLE AppHandle; EFI_HII_HANDLE HiiHandle; - SOVEREIGN_BOOT_WIZARD_CONFIG_DATA ConfigData; - SOVEREIGN_BOOT_WIZARD_NV_CONFIG NvConfig; - SOVEREIGN_BOOT_WIZARD_FORM_DATA FormData; + SOVEREIGN_BOOT_WIZARD_CONFIG_DATA ConfigData; + SOVEREIGN_BOOT_WIZARD_NV_CONFIG NvConfig; + SOVEREIGN_BOOT_WIZARD_FORM_DATA FormData; EFI_STRING_ID NameStringId[NAME_VALUE_NAME_NUMBER]; EFI_STRING NameValueName[NAME_VALUE_NAME_NUMBER]; @@ -130,22 +133,55 @@ typedef struct { EFI_STRING_ID DevicePathStringToken; EFI_STRING_ID FilePathStringToken; UINTN ContextSelection; - VOID *VariableContext; + VOID *VariableContext; // BM_LOAD_CONTEXT + VOID *SecurityContext; // BM_SECURITY_CONTEXT } BM_MENU_ENTRY; typedef struct { BOOLEAN IsBootNext; - BOOLEAN Deleted; + BOOLEAN NeedsPathExpansion; BOOLEAN IsLegacy; BOOLEAN IsFvOption; UINT32 Attributes; - UINT16 FilePathListLength; + UINT16 FilePathLength; UINT16 *Description; - EFI_DEVICE_PATH_PROTOCOL *FilePathList; + EFI_DEVICE_PATH_PROTOCOL *FilePath; } BM_LOAD_CONTEXT; +typedef struct { + UINT32 Signature; + + BOOLEAN CertIsInDbx; + BOOLEAN CertIsInDb; + BOOLEAN ImageIsVerified; + + UINTN CertDataSize; + UINT8 *CertData; + + UINTN CertDigestSize; + UINT8 *CertDigest; + + EFI_GUID CertType; + + LIST_ENTRY CertLink; +} BM_CERT_ENTRY; + +typedef struct { + BOOLEAN ImageIsInDbx; + BOOLEAN ImageIsInDb; + BOOLEAN ImageIsSigned; + + UINT32 AuthenticationStatus; + UINT32 NumCertificates; + + UINTN ImageDigestSize; + UINT8 *ImageDigest; + + LIST_ENTRY Certs; +} BM_SECURITY_CONTEXT; + typedef struct { EFI_HANDLE Handle; EFI_DEVICE_PATH_PROTOCOL *DevicePath; @@ -161,6 +197,8 @@ typedef struct { } BM_FILE_CONTEXT; extern BM_MENU_OPTION BootOptionMenu; +extern UINTN mBootloaderIndex; +extern UINTN mCertIndex; EFI_STATUS GetBootOptions ( @@ -175,8 +213,25 @@ GetMenuEntry ( EFI_STATUS UpdateBootloaderPage ( + IN SOVEREIGN_BOOT_WIZARD_PRIVATE_DATA *Private + ); + +EFI_STATUS +FillSecurityContext ( + IN BM_MENU_ENTRY *Entry + ); + +BM_CERT_ENTRY * +GetCertEntry ( + BM_MENU_ENTRY *MenuEntry, + UINTN CertNumber + ); + +EFI_STATUS +UpdateCertInfo ( IN SOVEREIGN_BOOT_WIZARD_PRIVATE_DATA *Private, - IN UINTN OptionNumber + IN UINTN OptionNumber, + IN UINTN CertNumber ); #endif diff --git a/DasharoModulePkg/Application/SovereignBootWizard/SovereignBootWizard.inf b/DasharoModulePkg/Application/SovereignBootWizard/SovereignBootWizard.inf index 9580849b00..907b95e897 100644 --- a/DasharoModulePkg/Application/SovereignBootWizard/SovereignBootWizard.inf +++ b/DasharoModulePkg/Application/SovereignBootWizard/SovereignBootWizard.inf @@ -27,6 +27,7 @@ [Sources] BootOptionParsing.c + SignatureParsing.c SovereignBootWizard.c SovereignBootWizardHii.h SovereignBootWizardVfrStrings.uni @@ -38,12 +39,14 @@ MdeModulePkg/MdeModulePkg.dec DasharoModulePkg/DasharoModulePkg.dec SecurityPkg/SecurityPkg.dec + CryptoPkg/CryptoPkg.dec [LibraryClasses] BaseLib BaseCryptLib MemoryAllocationLib UefiBootManagerLib + DxeServicesLib UefiBootServicesTableLib UefiApplicationEntryPoint UefiRuntimeServicesTableLib @@ -57,6 +60,7 @@ PeCoffLib SecureBootVariableLib SecureBootVariableProvisionLib + DxeImageVerificationLib [Guids] gSovereignBootWizardFormSetGuid diff --git a/DasharoModulePkg/Application/SovereignBootWizard/SovereignBootWizardHii.h b/DasharoModulePkg/Application/SovereignBootWizard/SovereignBootWizardHii.h index f77c2cdacd..c5597e0390 100644 --- a/DasharoModulePkg/Application/SovereignBootWizard/SovereignBootWizardHii.h +++ b/DasharoModulePkg/Application/SovereignBootWizard/SovereignBootWizardHii.h @@ -57,7 +57,7 @@ Revision History: // Form Data typedef struct { - UINT8 Unused; + BOOLEAN ImageUnsigned; } SOVEREIGN_BOOT_WIZARD_FORM_DATA; #pragma pack() diff --git a/DasharoModulePkg/Application/SovereignBootWizard/SovereignBootWizardVfr.vfr b/DasharoModulePkg/Application/SovereignBootWizard/SovereignBootWizardVfr.vfr index e9c65e1f87..d74d2e8dec 100644 --- a/DasharoModulePkg/Application/SovereignBootWizard/SovereignBootWizardVfr.vfr +++ b/DasharoModulePkg/Application/SovereignBootWizard/SovereignBootWizardVfr.vfr @@ -109,6 +109,7 @@ formset subtitle text = STRING_TOKEN(STR_EMPTY_STRING); subtitle text = STRING_TOKEN(STR_KEY_FINGERPRINT); + subtitle text = STRING_TOKEN(STR_KEY_FINGERPRINT_HASH); subtitle text = STRING_TOKEN(STR_EMPTY_STRING); subtitle text = STRING_TOKEN(STR_TRUST_QUESTION); @@ -134,11 +135,13 @@ formset key = TRUST_KEY_FORM2_QUESTION_ID; endif; + grayoutif ideqval SvBootFormData.ImageUnsigned == 1; text help = STRING_TOKEN(STR_EMPTY_STRING), text = STRING_TOKEN(STR_SHOW_KEY_DETAILS), flags = INTERACTIVE, key = SHOW_KEY_DETAILS_FORM2_QUESTION_ID; + endif; text help = STRING_TOKEN(STR_EMPTY_STRING), diff --git a/DasharoModulePkg/Application/SovereignBootWizard/SovereignBootWizardVfrStrings.uni b/DasharoModulePkg/Application/SovereignBootWizard/SovereignBootWizardVfrStrings.uni index a1700eb372..4b52426bfc 100644 --- a/DasharoModulePkg/Application/SovereignBootWizard/SovereignBootWizardVfrStrings.uni +++ b/DasharoModulePkg/Application/SovereignBootWizard/SovereignBootWizardVfrStrings.uni @@ -61,7 +61,8 @@ #string STR_BOOTOPT_DESCRIPTION #language en-US "Description: " #string STR_HW_PATH #language en-US "Hardware path: " #string STR_FILE_PATH #language en-US "File path: " -#string STR_KEY_FINGERPRINT #language en-US "Key fingerprint (SHA-256): " +#string STR_KEY_FINGERPRINT #language en-US "Certificate fingerprint (SHA-256):" +#string STR_KEY_FINGERPRINT_HASH #language en-US "" #string STR_TRUST_QUESTION #language en-US "Do you want to trust this key/image and continue booting?" From d6c56acfa177a5db7d1acd4561da558fbba3cb6b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20=C5=BBygowski?= Date: Tue, 22 Jul 2025 14:01:19 +0200 Subject: [PATCH 07/28] DMP/Application/SovereignBootWizard: Add confirmation pop-up and handle booting MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Michał Żygowski --- .../SovereignBootWizard/BootOptionParsing.c | 102 ++++++++++----- .../SovereignBootWizard/SignatureParsing.c | 115 ++++++++-------- .../SovereignBootWizard/SovereignBootWizard.c | 123 ++++++++++++++++++ .../SovereignBootWizard/SovereignBootWizard.h | 57 ++++---- .../SovereignBootWizardVfrStrings.uni | 15 ++- 5 files changed, 281 insertions(+), 131 deletions(-) diff --git a/DasharoModulePkg/Application/SovereignBootWizard/BootOptionParsing.c b/DasharoModulePkg/Application/SovereignBootWizard/BootOptionParsing.c index 38ebcab450..a08f407490 100644 --- a/DasharoModulePkg/Application/SovereignBootWizard/BootOptionParsing.c +++ b/DasharoModulePkg/Application/SovereignBootWizard/BootOptionParsing.c @@ -11,7 +11,7 @@ SPDX-License-Identifier: BSD-2-Clause-Patent /// /// Boot Option from variable Menu /// -BM_MENU_OPTION BootOptionMenu = { +SV_MENU_OPTION BootOptionMenu = { SOVEREIGN_BOOT_MENU_OPTION_SIGNATURE, { NULL }, 0 @@ -139,12 +139,12 @@ ExtractFilePath ( @return the new menu entry. **/ -BM_MENU_ENTRY * +SV_MENU_ENTRY * CreateMenuEntry ( UINTN MenuType ) { - BM_MENU_ENTRY *MenuEntry; + SV_MENU_ENTRY *MenuEntry; UINTN ContextSize; // @@ -152,11 +152,7 @@ CreateMenuEntry ( // switch (MenuType) { case SOVEREIGN_BOOT_LOAD_CONTEXT_SELECT: - ContextSize = sizeof (BM_LOAD_CONTEXT); - break; - - case SOVEREIGN_BOOT_FILE_CONTEXT_SELECT: - ContextSize = sizeof (BM_FILE_CONTEXT); + ContextSize = sizeof (SV_LOAD_CONTEXT); break; default: @@ -171,7 +167,7 @@ CreateMenuEntry ( // // Create new menu entry // - MenuEntry = AllocateZeroPool (sizeof (BM_MENU_ENTRY)); + MenuEntry = AllocateZeroPool (sizeof (SV_MENU_ENTRY)); if (MenuEntry == NULL) { return NULL; } @@ -210,9 +206,12 @@ GetBootOptions ( UINT16 *BootOrderList; UINTN BootOrderListSize; UINT8 *LoadOptionPtr; - BM_MENU_ENTRY *NewMenuEntry; - BM_LOAD_CONTEXT *NewLoadContext; + UINT8 *LoadOptionEnd; + SV_MENU_ENTRY *NewMenuEntry; + SV_LOAD_CONTEXT *NewLoadContext; + UINTN OptionalDataSize; UINTN StringSize; + UINTN DescriptionSize; EFI_DEVICE_PATH_PROTOCOL *DevicePath; EFI_DEVICE_PATH_PROTOCOL *HwDevicePath; UINTN MenuCount; @@ -272,7 +271,8 @@ GetBootOptions ( // // Description = (CHAR16 *)Ptr; // - Ptr += StrSize ((CHAR16 *)Ptr); + DescriptionSize = StrSize ((CHAR16 *)Ptr); + Ptr += DescriptionSize; // // Now Ptr point to Device Path @@ -286,11 +286,14 @@ GetBootOptions ( } NewMenuEntry = CreateMenuEntry (SOVEREIGN_BOOT_LOAD_CONTEXT_SELECT); - ASSERT (NULL != NewMenuEntry); + if (NewMenuEntry == NULL) { + return EFI_OUT_OF_RESOURCES; + } - NewLoadContext = (BM_LOAD_CONTEXT *)NewMenuEntry->VariableContext; + NewLoadContext = (SV_LOAD_CONTEXT *)NewMenuEntry->VariableContext; LoadOptionPtr = LoadOptionFromVar; + LoadOptionEnd = LoadOptionFromVar + BootOptionSize; NewMenuEntry->OptionNumber = BootOrderList[Index]; @@ -305,7 +308,6 @@ GetBootOptions ( // for easy use with following LOAD_OPTION // embedded in this struct // - NewLoadContext->Attributes = *(UINT32 *)LoadOptionPtr; LoadOptionPtr += sizeof (UINT32); @@ -313,9 +315,12 @@ GetBootOptions ( NewLoadContext->FilePathLength = *(UINT16 *)LoadOptionPtr; LoadOptionPtr += sizeof (UINT16); - StringSize = StrSize (L"Description: ") + StrSize ((UINT16 *)LoadOptionPtr) + sizeof(CHAR16); + StringSize = StrSize (L"Description: ") + DescriptionSize + sizeof(CHAR16); NewLoadContext->Description = AllocateZeroPool (StringSize); - ASSERT (NewLoadContext->Description != NULL); + if (NewLoadContext->Description == NULL) { + return EFI_OUT_OF_RESOURCES; + } + UnicodeSPrint (NewLoadContext->Description, StringSize, L"Description: %s", LoadOptionPtr); NewMenuEntry->DisplayString = NewLoadContext->Description; @@ -324,13 +329,38 @@ GetBootOptions ( LoadOptionPtr += StrSize ((UINT16 *)LoadOptionPtr); NewLoadContext->FilePath = AllocateZeroPool (NewLoadContext->FilePathLength); - ASSERT (NewLoadContext->FilePath != NULL); + if (NewLoadContext->FilePath == NULL) { + return EFI_OUT_OF_RESOURCES; + } + CopyMem ( NewLoadContext->FilePath, (EFI_DEVICE_PATH_PROTOCOL *)LoadOptionPtr, NewLoadContext->FilePathLength ); + LoadOptionPtr += NewLoadContext->FilePathLength; + + if (LoadOptionPtr < LoadOptionEnd) { + OptionalDataSize = BootOptionSize - + sizeof (UINT32) - + sizeof (UINT16) - + DescriptionSize - + NewLoadContext->FilePathLength; + NewLoadContext->OptionalData = AllocateZeroPool (OptionalDataSize); + + if (NewLoadContext->OptionalData == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + CopyMem ( + NewLoadContext->OptionalData, + LoadOptionPtr, + OptionalDataSize + ); + NewLoadContext->OptionalDataSize = OptionalDataSize; + } + // Hardware Path to the disk HwDevicePath = StripFilePath (NewLoadContext->FilePath); if (HwDevicePath == NULL) { @@ -352,24 +382,28 @@ GetBootOptions ( // File path on the disk if (HwDevicePath != NULL) { PathString = UiDevicePathToStr (Private->DevPathToText, ExtractFilePath (NewLoadContext->FilePath)); - ASSERT (PathString != NULL); + if (PathString == NULL) { + return EFI_OUT_OF_RESOURCES; + } } else { // In case there is no file device path, it means it is /EFI/BOOT/BOOTX64.efi - // and is automatically expanded by UEFI boot manager + // and is automatically expanded by UEFI boot manager. However when reading + // the file in the application, we have to expand it ourselves. PathString = EFI_REMOVABLE_MEDIA_FILE_NAME; NewLoadContext->NeedsPathExpansion = TRUE; } StringSize = StrSize (L"File path: ") + StrSize (PathString) + sizeof(CHAR16); NewMenuEntry->FilePathString = AllocateZeroPool (StringSize); - ASSERT (NewMenuEntry->FilePathString != NULL); + if (NewMenuEntry->FilePathString == NULL) { + return EFI_OUT_OF_RESOURCES; + } + UnicodeSPrint (NewMenuEntry->FilePathString, StringSize, L"File path: %s", PathString); if (HwDevicePath != NULL) { FreePool (PathString); } NewMenuEntry->FilePathStringToken = HiiSetString (Private->HiiHandle, 0, NewMenuEntry->FilePathString, NULL); - FillSecurityContext(NewMenuEntry); - InsertTailList (&BootOptionMenu.Head, &NewMenuEntry->Link); MenuCount++; FreePool (LoadOptionFromVar); @@ -399,13 +433,13 @@ GetBootOptions ( @return The Menu Entry. **/ -BM_MENU_ENTRY * +SV_MENU_ENTRY * GetMenuEntry ( - BM_MENU_OPTION *MenuOption, + SV_MENU_OPTION *MenuOption, UINTN MenuNumber ) { - BM_MENU_ENTRY *NewMenuEntry; + SV_MENU_ENTRY *NewMenuEntry; UINTN Index; LIST_ENTRY *List; @@ -418,7 +452,7 @@ GetMenuEntry ( List = List->ForwardLink; } - NewMenuEntry = CR (List, BM_MENU_ENTRY, Link, SOVEREIGN_BOOT_MENU_ENTRY_SIGNATURE); + NewMenuEntry = CR (List, SV_MENU_ENTRY, Link, SOVEREIGN_BOOT_MENU_ENTRY_SIGNATURE); return NewMenuEntry; } @@ -428,8 +462,8 @@ UpdateBootloaderPage ( IN SOVEREIGN_BOOT_WIZARD_PRIVATE_DATA *Private ) { - BM_MENU_ENTRY *BootloaderEntry; - BM_LOAD_CONTEXT *BootloaderContext; + SV_MENU_ENTRY *BootloaderEntry; + SV_LOAD_CONTEXT *BootloaderContext; EFI_STRING NewString; EFI_STATUS Status; @@ -438,7 +472,7 @@ UpdateBootloaderPage ( return EFI_NO_MEDIA; } - BootloaderContext = (BM_LOAD_CONTEXT *)BootloaderEntry->VariableContext; + BootloaderContext = (SV_LOAD_CONTEXT *)BootloaderEntry->VariableContext; if (BootloaderContext == NULL) { return EFI_NO_MEDIA; } @@ -465,6 +499,14 @@ UpdateBootloaderPage ( HiiSetString (Private->HiiHandle, STRING_TOKEN (STR_BOOTOPT_DESCRIPTION), L"Not Found!", NULL); } + // Filling security context is time-consuming and may cause unnecessary + // delays. Also we should parse the certificates dynamically as user gives + // their choices, so that the keys appearing will always have up to date + // state (trusted/untrusted), so they can be skipped. + if (BootloaderEntry->SecurityContext == NULL) { + FillSecurityContext(BootloaderEntry); + } + Status = UpdateCertInfo (Private, mBootloaderIndex, mCertIndex); mCertIndex++; if (Status == EFI_NO_MEDIA || ((mCertIndex > 1) && Private->FormData.ImageUnsigned)) { diff --git a/DasharoModulePkg/Application/SovereignBootWizard/SignatureParsing.c b/DasharoModulePkg/Application/SovereignBootWizard/SignatureParsing.c index 66460e08aa..9f8dcd71c8 100644 --- a/DasharoModulePkg/Application/SovereignBootWizard/SignatureParsing.c +++ b/DasharoModulePkg/Application/SovereignBootWizard/SignatureParsing.c @@ -50,10 +50,10 @@ VOID VerifyImageHashInDatabases ( IN VOID *FileBuffer, IN UINTN FileSize, - IN BM_SECURITY_CONTEXT *SecCtx + IN SV_SECURITY_CONTEXT *SecCtx ) { - EFI_STATUS DbStatus; + EFI_STATUS Status; BOOLEAN IsFound; UINT8 HashAlg; UINT8 ImageDigest[MAX_DIGEST_SIZE]; @@ -68,26 +68,26 @@ VerifyImageHashInDatabases ( continue; } - DbStatus = IsSignatureFoundInDatabase ( + Status = IsSignatureFoundInDatabase ( EFI_IMAGE_SECURITY_DATABASE1, ImageDigest, &CertType, ImageDigestSize, &IsFound ); - if (!EFI_ERROR (DbStatus) || IsFound) { - SecCtx->ImageIsInDb = TRUE; + if (!EFI_ERROR (Status) && IsFound) { + SecCtx->ImageIsInDbx = TRUE; } - DbStatus = IsSignatureFoundInDatabase ( + Status = IsSignatureFoundInDatabase ( EFI_IMAGE_SECURITY_DATABASE, ImageDigest, &CertType, ImageDigestSize, &IsFound ); - if (!EFI_ERROR (DbStatus) && IsFound) { - SecCtx->ImageIsInDbx = TRUE; + if (!EFI_ERROR (Status) && IsFound) { + SecCtx->ImageIsInDb = TRUE; } } } @@ -96,7 +96,7 @@ VOID FillCertificateEntries ( IN VOID *FileBuffer, IN UINTN FileSize, - IN OUT BM_SECURITY_CONTEXT *SecCtx, + IN OUT SV_SECURITY_CONTEXT *SecCtx, IN EFI_IMAGE_DATA_DIRECTORY *SecDataDir ) { @@ -111,7 +111,7 @@ FillCertificateEntries ( EFI_STATUS HashStatus; UINT8 ImageDigest[MAX_DIGEST_SIZE]; UINTN ImageDigestSize; - BM_CERT_ENTRY *NewCertEntry; + SV_CERT_ENTRY *NewCertEntry; UINT8 *CertBuffer; UINTN BufferLength; UINT8 *TrustedCert; @@ -187,7 +187,7 @@ FillCertificateEntries ( continue; } - NewCertEntry = (BM_CERT_ENTRY *) AllocateZeroPool (sizeof(BM_CERT_ENTRY)); + NewCertEntry = (SV_CERT_ENTRY *) AllocateZeroPool (sizeof(SV_CERT_ENTRY)); if (NewCertEntry == NULL) { DEBUG ((EFI_D_ERROR, "Not enough free memory for certificate data\n")); @@ -210,21 +210,17 @@ FillCertificateEntries ( continue; } + // Free unused cert chain + Pkcs7FreeSigners (CertBuffer); + // Save the buffer to the signer's certificate NewCertEntry->CertData = TrustedCert; NewCertEntry->CertDataSize = TrustedCertLength; - // Free unused cert chain - Pkcs7FreeSigners (CertBuffer); - - NewCertEntry->CertDigest = (UINT8 *)AllocateZeroPool(SHA256_DIGEST_SIZE); - if (NewCertEntry->CertDigest != NULL) { - CalculateCertHash( - NewCertEntry->CertData, - NewCertEntry->CertDataSize, - HASHALG_SHA256, - NewCertEntry->CertDigest); + if (CalculateCertHash (TrustedCert, TrustedCertLength, HASHALG_SHA256, NewCertEntry->CertDigest)) { NewCertEntry->CertDigestSize = SHA256_DIGEST_SIZE; + } else { + DEBUG ((EFI_D_INFO, "Could not calculate TBS certificate hash\n")); } HashStatus = HashPeImageByType ( @@ -239,26 +235,19 @@ FillCertificateEntries ( if (TrustedCert != NULL) { Pkcs7FreeSigners (TrustedCert); } - FreePool (NewCertEntry->CertDigest); FreePool (NewCertEntry); DEBUG ((EFI_D_ERROR, "Failed to calculate image hash\n")); continue; } - // - // Check the digital signature against the valid certificate in allowed database (db). - if (IsAllowedByDb (AuthData, AuthDataSize, ImageDigest, ImageDigestSize)) { - NewCertEntry->ImageIsVerified = TRUE; - NewCertEntry->CertIsInDb = TRUE; - } + // Verify the digital signature amnd check against databases + NewCertEntry->CertIsInDb = IsAllowedByDb (AuthData, AuthDataSize, ImageDigest, ImageDigestSize); + NewCertEntry->CertIsInDbx = IsForbiddenByDbx (AuthData, AuthDataSize, ImageDigest, ImageDigestSize); + NewCertEntry->ImageIsVerified = AuthenticodeVerify ( + AuthData, AuthDataSize, + TrustedCert, TrustedCertLength, + ImageDigest, ImageDigestSize); - // - // Check the digital signature against the revoked certificate in forbidden database (dbx). - // - if (IsForbiddenByDbx (AuthData, AuthDataSize, ImageDigest, ImageDigestSize)) { - NewCertEntry->ImageIsVerified = FALSE; - NewCertEntry->CertIsInDbx = TRUE; - } DEBUG ((EFI_D_INFO, "Certificate Details:\n" "\tImageIsVerified: %u\n" @@ -291,26 +280,23 @@ FillCertificateEntries ( **/ EFI_STATUS FillSecurityContext ( - IN BM_MENU_ENTRY *Entry + IN SV_MENU_ENTRY *Entry ) { - BM_SECURITY_CONTEXT *SecCtx; - BM_LOAD_CONTEXT *LoadCtx; + SV_SECURITY_CONTEXT *SecCtx; + SV_LOAD_CONTEXT *LoadCtx; EFI_IMAGE_DATA_DIRECTORY *SecDir; EFI_DEVICE_PATH_PROTOCOL *FullFilePath; VOID *ImageBase; UINTN ImageSize; UINT32 AuthStatus; EFI_STATUS Status; - EFI_GUID CertType; FullFilePath = NULL; - LoadCtx = (BM_LOAD_CONTEXT *)Entry->VariableContext; - SecCtx = (BM_SECURITY_CONTEXT *)AllocateZeroPool(sizeof(BM_SECURITY_CONTEXT)); + LoadCtx = (SV_LOAD_CONTEXT *)Entry->VariableContext; + SecCtx = (SV_SECURITY_CONTEXT *)AllocateZeroPool(sizeof(SV_SECURITY_CONTEXT)); Entry->SecurityContext = SecCtx; - DEBUG ((EFI_D_INFO, "%a: Processing %s\n", __FUNCTION__, LoadCtx->Description)); - if (SecCtx == NULL) { DEBUG ((EFI_D_INFO, "Not enough memory for security context\n")); return EFI_OUT_OF_RESOURCES; @@ -352,8 +338,12 @@ FillSecurityContext ( // Always calculate SHA256 hash of the image and store it for display and // possible trust/distrust choice. - SecCtx->ImageDigest = (UINT8 *)AllocateZeroPool(SHA256_DIGEST_SIZE); - HashPeImage (ImageBase, ImageSize, HASHALG_SHA256, SecCtx->ImageDigest, &SecCtx->ImageDigestSize, &CertType); + HashPeImage (ImageBase, + ImageSize, + HASHALG_SHA256, + SecCtx->ImageDigest, + &SecCtx->ImageDigestSize, + &SecCtx->HashType); // // Start Image Validation. @@ -375,20 +365,19 @@ FillSecurityContext ( FillCertificateEntries (ImageBase, ImageSize, SecCtx, SecDir); Status = EFI_SUCCESS; +ON_EXIT: DEBUG ((EFI_D_INFO, "Image Details:\n" - "\tImageIsInDbx: %u\n" - "\tImageIsInDb: %u\n" - "\tImageIsSigned: %u\n" - "\tAuthenticationStatus: %u\n" - "\tNumCertificates: %u\n", + " ImageIsInDbx: %u\n" + " ImageIsInDb: %u\n" + " ImageIsSigned: %u\n" + " AuthenticationStatus: %u\n" + " NumCertificates: %u\n", SecCtx->ImageIsInDbx, SecCtx->ImageIsInDb, SecCtx->ImageIsSigned, SecCtx->AuthenticationStatus, SecCtx->NumCertificates)); -ON_EXIT: - if (ImageBase != NULL) { FreePool (ImageBase); } @@ -408,18 +397,18 @@ FillSecurityContext ( @return The Menu Entry. **/ -BM_CERT_ENTRY * +SV_CERT_ENTRY * GetCertEntry ( - BM_MENU_ENTRY *MenuEntry, + SV_MENU_ENTRY *MenuEntry, UINTN CertNumber ) { - BM_CERT_ENTRY *NewCertEntry; - BM_SECURITY_CONTEXT *SecCtx; + SV_CERT_ENTRY *NewCertEntry; + SV_SECURITY_CONTEXT *SecCtx; UINTN Index; LIST_ENTRY *List; - SecCtx = (BM_SECURITY_CONTEXT *) MenuEntry->SecurityContext; + SecCtx = (SV_SECURITY_CONTEXT *) MenuEntry->SecurityContext; if (SecCtx == NULL) { return NULL; @@ -434,7 +423,7 @@ GetCertEntry ( List = List->ForwardLink; } - NewCertEntry = CR (List, BM_CERT_ENTRY, CertLink, SOVEREIGN_BOOT_CERT_ENTRY_SIGNATURE); + NewCertEntry = CR (List, SV_CERT_ENTRY, CertLink, SOVEREIGN_BOOT_CERT_ENTRY_SIGNATURE); return NewCertEntry; } @@ -490,9 +479,9 @@ UpdateCertInfo ( IN UINTN CertNumber ) { - BM_MENU_ENTRY *BootloaderEntry; - BM_CERT_ENTRY *CertificateEntry; - BM_SECURITY_CONTEXT *SecurityContext; + SV_MENU_ENTRY *BootloaderEntry; + SV_CERT_ENTRY *CertificateEntry; + SV_SECURITY_CONTEXT *SecurityContext; EFI_STRING NewString; EFI_STRING OldString; EFI_STATUS Status; @@ -502,7 +491,7 @@ UpdateCertInfo ( return EFI_NO_MEDIA; } - SecurityContext = (BM_SECURITY_CONTEXT *)BootloaderEntry->SecurityContext; + SecurityContext = (SV_SECURITY_CONTEXT *)BootloaderEntry->SecurityContext; if (SecurityContext == NULL) { return EFI_NO_MEDIA; } @@ -514,7 +503,7 @@ UpdateCertInfo ( // Image is unsigned? Show its hash instead of certificates if (Private->FormData.ImageUnsigned) { - HiiSetString (Private->HiiHandle, STRING_TOKEN (STR_KEY_FINGERPRINT), L"Image is unsigned !!!\nImage hash (SHA-256):", NULL); + HiiSetString (Private->HiiHandle, STRING_TOKEN (STR_KEY_FINGERPRINT), L"Image hash (SHA-256):\n!!! Image is unsigned !!!", NULL); Status = ParseHashValue (SecurityContext->ImageDigest, SecurityContext->ImageDigestSize, &NewString); if (!EFI_ERROR (Status) && (NewString != NULL)) { diff --git a/DasharoModulePkg/Application/SovereignBootWizard/SovereignBootWizard.c b/DasharoModulePkg/Application/SovereignBootWizard/SovereignBootWizard.c index 62f2da523b..12bcb93e27 100644 --- a/DasharoModulePkg/Application/SovereignBootWizard/SovereignBootWizard.c +++ b/DasharoModulePkg/Application/SovereignBootWizard/SovereignBootWizard.c @@ -344,6 +344,110 @@ PrepareSbVariablesForSvBoot ( return SetSecureBootMode (STANDARD_SECURE_BOOT_MODE); } +EFI_STATUS +AddKeyOrHashAsTrustedOrUntrusted ( + SOVEREIGN_BOOT_WIZARD_PRIVATE_DATA *PrivateData, + BOOLEAN Trust + ) +{ + EFI_STRING Message; + EFI_HII_POPUP_SELECTION UserSelection; + EFI_STRING HeaderString; + EFI_STRING HashString; + EFI_STRING PopupMessage; + UINTN PopupMessageSize; + EFI_STATUS Status; + + + Message = HiiGetString ( + PrivateData->HiiHandle, + Trust ? STRING_TOKEN(STR_SV_TRUST_KEY_QUESTION) : STRING_TOKEN(STR_SV_UNTRUST_KEY_QUESTION), + NULL); + HeaderString = HiiGetString (PrivateData->HiiHandle, STRING_TOKEN(STR_KEY_FINGERPRINT), NULL); + HashString = HiiGetString (PrivateData->HiiHandle, STRING_TOKEN(STR_KEY_FINGERPRINT_HASH), NULL); + + // Size of the whole message plus couple new lines + PopupMessageSize = StrSize(Message) + StrSize(HeaderString) + StrSize(HashString) + 6; + PopupMessage = (CHAR16 *) AllocateZeroPool(PopupMessageSize); + + if (PopupMessage == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + UnicodeSPrint (PopupMessage, PopupMessageSize, L"%s\n%s\n%s", Message, HeaderString, HashString); + HiiSetString (PrivateData->HiiHandle, STRING_TOKEN (STR_SV_TRUST_KEY_POPUP), PopupMessage, NULL) ; + + Status = PrivateData->HiiPopup->CreatePopup ( + PrivateData->HiiPopup, + EfiHiiPopupStyleInfo, + EfiHiiPopupTypeYesNo, + PrivateData->HiiHandle, + STRING_TOKEN (STR_SV_TRUST_KEY_POPUP), + &UserSelection + ); + if (UserSelection == EfiHiiPopupSelectionYes) { + // Add key or hash to DB or DBX + if (Trust) { + + } else { + + } + } + + FreePool (PopupMessage); + + return Status; +} + +EFI_STATUS +BootTheBootloader ( + SOVEREIGN_BOOT_WIZARD_PRIVATE_DATA *PrivateData + ) +{ + SV_MENU_ENTRY *BootloaderEntry; + SV_LOAD_CONTEXT *BootloaderContext; + EFI_BOOT_MANAGER_LOAD_OPTION BootOption; + EFI_STATUS Status; + + BootloaderEntry = GetMenuEntry (&BootOptionMenu, mBootloaderIndex); + if (BootloaderEntry == NULL) { + return EFI_NO_MEDIA; + } + + BootloaderContext = (SV_LOAD_CONTEXT *)BootloaderEntry->VariableContext; + if (BootloaderContext == NULL) { + return EFI_NO_MEDIA; + } + + Status = EfiBootManagerInitializeLoadOption ( + &BootOption, + LoadOptionNumberUnassigned, + LoadOptionTypeBoot, + LOAD_OPTION_ACTIVE, + BootloaderContext->Description, + BootloaderContext->FilePath, + BootloaderContext->OptionalData, + BootloaderContext->OptionalDataSize + ); + + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_INFO, "Failed to prepare load option: %r\n", Status)); + return Status; + } + + if (gST->ConOut != NULL) { + gST->ConOut->ClearScreen (gST->ConOut); + } + + DEBUG ((EFI_D_INFO, "Booting %s\n", BootloaderContext->Description)); + EfiBootManagerBoot (&BootOption); + EfiBootManagerFreeLoadOption (&BootOption); + + // In case we get back from the booted bootloader, we have to update the + // page so that it will display next bootloader or key. + return UpdateBootloaderPage (PrivateData); +} + /** This function processes the results of changes in configuration. @@ -449,6 +553,19 @@ Callback ( Status = PrepareSbVariablesForSvBoot (); break; } + case DO_NOT_TRUST_KEY_FORM2_QUESTION_ID: + { + // Add cert or image hash to DBX + Status = AddKeyOrHashAsTrustedOrUntrusted(PrivateData, FALSE); + break; + } + case TRUST_KEY_AND_BOOT_FORM2_QUESTION_ID: + case TRUST_KEY_FORM2_QUESTION_ID: + { + // Add cert or image hash to DB + Status = AddKeyOrHashAsTrustedOrUntrusted(PrivateData, TRUE); + break; + } default: break; } @@ -538,6 +655,12 @@ Callback ( Status = EFI_NO_MEDIA; } break; + case TRUST_KEY_AND_BOOT_FORM2_QUESTION_ID: + // All is left here is to enroll PK to enable Secure Boot, set + // Sovereign Boot to provisioned and boot. + + Status = BootTheBootloader (PrivateData); + break; default: break; } diff --git a/DasharoModulePkg/Application/SovereignBootWizard/SovereignBootWizard.h b/DasharoModulePkg/Application/SovereignBootWizard/SovereignBootWizard.h index 5c3c03ddc8..2649295b1c 100644 --- a/DasharoModulePkg/Application/SovereignBootWizard/SovereignBootWizard.h +++ b/DasharoModulePkg/Application/SovereignBootWizard/SovereignBootWizard.h @@ -120,7 +120,7 @@ typedef struct { UINTN Signature; LIST_ENTRY Head; UINTN MenuNumber; -} BM_MENU_OPTION; +} SV_MENU_OPTION; typedef struct { UINTN Signature; @@ -133,9 +133,9 @@ typedef struct { EFI_STRING_ID DevicePathStringToken; EFI_STRING_ID FilePathStringToken; UINTN ContextSelection; - VOID *VariableContext; // BM_LOAD_CONTEXT - VOID *SecurityContext; // BM_SECURITY_CONTEXT -} BM_MENU_ENTRY; + VOID *VariableContext; // SV_LOAD_CONTEXT + VOID *SecurityContext; // SV_SECURITY_CONTEXT +} SV_MENU_ENTRY; typedef struct { BOOLEAN IsBootNext; @@ -148,7 +148,9 @@ typedef struct { UINT16 FilePathLength; UINT16 *Description; EFI_DEVICE_PATH_PROTOCOL *FilePath; -} BM_LOAD_CONTEXT; + UINT8 *OptionalData; + UINTN OptionalDataSize; +} SV_LOAD_CONTEXT; typedef struct { UINT32 Signature; @@ -156,19 +158,25 @@ typedef struct { BOOLEAN CertIsInDbx; BOOLEAN CertIsInDb; BOOLEAN ImageIsVerified; + BOOLEAN CertIsMicrosoft; + + // Placed exactly at offset 8 for alignment + UINT8 CertDigest[MAX_DIGEST_SIZE]; + UINTN CertDigestSize; UINTN CertDataSize; UINT8 *CertData; - UINTN CertDigestSize; - UINT8 *CertDigest; - EFI_GUID CertType; LIST_ENTRY CertLink; -} BM_CERT_ENTRY; +} SV_CERT_ENTRY; typedef struct { + // Place first for 8 byte alignment + UINT8 ImageDigest[MAX_DIGEST_SIZE]; + UINTN ImageDigestSize; + BOOLEAN ImageIsInDbx; BOOLEAN ImageIsInDb; BOOLEAN ImageIsSigned; @@ -176,27 +184,12 @@ typedef struct { UINT32 AuthenticationStatus; UINT32 NumCertificates; - UINTN ImageDigestSize; - UINT8 *ImageDigest; + EFI_GUID HashType; LIST_ENTRY Certs; -} BM_SECURITY_CONTEXT; +} SV_SECURITY_CONTEXT; -typedef struct { - EFI_HANDLE Handle; - EFI_DEVICE_PATH_PROTOCOL *DevicePath; - EFI_FILE_HANDLE FHandle; - UINT16 *FileName; - EFI_FILE_SYSTEM_VOLUME_LABEL *Info; - - BOOLEAN IsRoot; - BOOLEAN IsDir; - BOOLEAN IsRemovableMedia; - BOOLEAN IsLoadFile; - BOOLEAN IsBootLegacy; -} BM_FILE_CONTEXT; - -extern BM_MENU_OPTION BootOptionMenu; +extern SV_MENU_OPTION BootOptionMenu; extern UINTN mBootloaderIndex; extern UINTN mCertIndex; @@ -205,9 +198,9 @@ GetBootOptions ( IN SOVEREIGN_BOOT_WIZARD_PRIVATE_DATA *Private ); -BM_MENU_ENTRY * +SV_MENU_ENTRY * GetMenuEntry ( - BM_MENU_OPTION *MenuOption, + SV_MENU_OPTION *MenuOption, UINTN MenuNumber ); @@ -218,12 +211,12 @@ UpdateBootloaderPage ( EFI_STATUS FillSecurityContext ( - IN BM_MENU_ENTRY *Entry + IN SV_MENU_ENTRY *Entry ); -BM_CERT_ENTRY * +SV_CERT_ENTRY * GetCertEntry ( - BM_MENU_ENTRY *MenuEntry, + SV_MENU_ENTRY *MenuEntry, UINTN CertNumber ); diff --git a/DasharoModulePkg/Application/SovereignBootWizard/SovereignBootWizardVfrStrings.uni b/DasharoModulePkg/Application/SovereignBootWizard/SovereignBootWizardVfrStrings.uni index 4b52426bfc..d82acea059 100644 --- a/DasharoModulePkg/Application/SovereignBootWizard/SovereignBootWizardVfrStrings.uni +++ b/DasharoModulePkg/Application/SovereignBootWizard/SovereignBootWizardVfrStrings.uni @@ -23,7 +23,7 @@ #string STR_FORM_SET_TITLE #language en-US "Sovereign Boot Provisioning Wizard" #string STR_FORM_SET_TITLE_HELP #language en-US " " #string STR_EMPTY_STRING #language en-US " " -#string STR_EXIT_TEXT #language en-US "[Exit]" +#string STR_EXIT_TEXT #language en-US "[ Exit ]" #string FUNCTION_NINE_STRING #language en-US "F9=Reset to Defaults" #string FUNCTION_TEN_STRING #language en-US "F10=Save" @@ -65,12 +65,15 @@ #string STR_KEY_FINGERPRINT_HASH #language en-US "" #string STR_TRUST_QUESTION #language en-US "Do you want to trust this key/image and continue booting?" +#string STR_SV_TRUST_KEY_POPUP #language en-US " " // will be updated dynamically with a hash +#string STR_SV_TRUST_KEY_QUESTION #language en-US "Are you sure you want to trust the following" +#string STR_SV_UNTRUST_KEY_QUESTION #language en-US "Are you sure you do NOT want to trust the following" -#string STR_DO_NOT_TRUST_KEY #language en-US "[Do NOT trust, next key/bootloader]" -#string STR_DO_NOT_TRUST_KEY2 #language en-US "[Do NOT trust]" -#string STR_TRUST_KEY_AND_BOOT #language en-US "[Trust this key/image and boot]" -#string STR_TRUST_KEY #language en-US "[Trust this key/image, next key/bootloader]" -#string STR_SHOW_KEY_DETAILS #language en-US "[Show key/certificate details]" +#string STR_DO_NOT_TRUST_KEY #language en-US "[ Do NOT trust, next key/bootloader ]" +#string STR_DO_NOT_TRUST_KEY2 #language en-US "[ Do NOT trust ]" +#string STR_TRUST_KEY_AND_BOOT #language en-US "[ Trust this key/image and boot ]" +#string STR_TRUST_KEY #language en-US "[ Trust this key/image, next key/bootloader ]" +#string STR_SHOW_KEY_DETAILS #language en-US "[ Show key/certificate details ]" // Interactive mode strings #string STR_FORM9_TITLE #language en-US "Sovereign Boot Provisioning Wizard" From 44217c5db4067708442864dd739d7e5af52af9f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20=C5=BBygowski?= Date: Wed, 23 Jul 2025 17:51:28 +0200 Subject: [PATCH 08/28] DMP/Application/SovereignBootWizard: Add enrolling certs to DB/DBX MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Michał Żygowski --- .../SovereignBootWizard/BootOptionParsing.c | 59 ++-- .../SovereignBootWizard/SignatureParsing.c | 102 ++++-- .../SovereignBootWizard/SovereignBootWizard.c | 314 +++++++++++++++++- .../SovereignBootWizard/SovereignBootWizard.h | 7 +- .../SovereignBootWizard.inf | 1 + 5 files changed, 417 insertions(+), 66 deletions(-) diff --git a/DasharoModulePkg/Application/SovereignBootWizard/BootOptionParsing.c b/DasharoModulePkg/Application/SovereignBootWizard/BootOptionParsing.c index a08f407490..cc7ea93da3 100644 --- a/DasharoModulePkg/Application/SovereignBootWizard/BootOptionParsing.c +++ b/DasharoModulePkg/Application/SovereignBootWizard/BootOptionParsing.c @@ -415,7 +415,7 @@ GetBootOptions ( FreePool (BootOrderList); } - DEBUG ((EFI_D_INFO, "Found %d boot options \n", MenuCount)); + DEBUG ((DEBUG_INFO, "Found %d boot options \n", MenuCount)); BootOptionMenu.MenuNumber = MenuCount; return EFI_SUCCESS; @@ -463,17 +463,42 @@ UpdateBootloaderPage ( ) { SV_MENU_ENTRY *BootloaderEntry; - SV_LOAD_CONTEXT *BootloaderContext; EFI_STRING NewString; EFI_STATUS Status; - BootloaderEntry = GetMenuEntry (&BootOptionMenu, mBootloaderIndex); - if (BootloaderEntry == NULL) { - return EFI_NO_MEDIA; + while (mBootloaderIndex < BootOptionMenu.MenuNumber) { + BootloaderEntry = GetMenuEntry (&BootOptionMenu, mBootloaderIndex); + if (BootloaderEntry == NULL) { + return EFI_NO_MEDIA; + } + + // Filling security context is time-consuming and may cause unnecessary + // delays. Also we should parse the certificates dynamically as user gives + // their choices, so that the keys appearing will always have up to date + // state (trusted/untrusted), so they can be skipped. + if (BootloaderEntry->SecurityContext == NULL) { + Status = FillSecurityContext(BootloaderEntry); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "Failed to fill security context for bootloader %u\n", mBootloaderIndex)); + return EFI_NO_MEDIA; + } + } + + Status = UpdateCertInfo (Private, mBootloaderIndex); + DEBUG ((DEBUG_INFO, "UpdateCertInfo state: %r\n", Status)); + if (Status == EFI_NO_MEDIA) { + DEBUG ((DEBUG_INFO, "No more keys/certificates for bootloader %u\n", mBootloaderIndex)); + // No more keys/certs to show for this bootloader, proceed to the next one + mCertIndex = 0; + mBootloaderIndex++; + continue; + } + + break; } - BootloaderContext = (SV_LOAD_CONTEXT *)BootloaderEntry->VariableContext; - if (BootloaderContext == NULL) { + if (mBootloaderIndex >= BootOptionMenu.MenuNumber) { + DEBUG ((DEBUG_INFO, "No more keys/certificates/bootloaders to show\n")); return EFI_NO_MEDIA; } @@ -499,24 +524,6 @@ UpdateBootloaderPage ( HiiSetString (Private->HiiHandle, STRING_TOKEN (STR_BOOTOPT_DESCRIPTION), L"Not Found!", NULL); } - // Filling security context is time-consuming and may cause unnecessary - // delays. Also we should parse the certificates dynamically as user gives - // their choices, so that the keys appearing will always have up to date - // state (trusted/untrusted), so they can be skipped. - if (BootloaderEntry->SecurityContext == NULL) { - FillSecurityContext(BootloaderEntry); - } - - Status = UpdateCertInfo (Private, mBootloaderIndex, mCertIndex); - mCertIndex++; - if (Status == EFI_NO_MEDIA || ((mCertIndex > 1) && Private->FormData.ImageUnsigned)) { - DEBUG ((EFI_D_INFO, "No more keys/certs for bootloader %u (current certificate %u)\n", - mBootloaderIndex, mCertIndex - 1)); - // No more keys/certs to show for this bootloader, proceed to the next one - mCertIndex = 0; - mBootloaderIndex++; - Status = UpdateBootloaderPage(Private); - } - + DEBUG ((DEBUG_INFO, "Bootloader state %r\n", Status)); return Status; } diff --git a/DasharoModulePkg/Application/SovereignBootWizard/SignatureParsing.c b/DasharoModulePkg/Application/SovereignBootWizard/SignatureParsing.c index 9f8dcd71c8..b160dc07d4 100644 --- a/DasharoModulePkg/Application/SovereignBootWizard/SignatureParsing.c +++ b/DasharoModulePkg/Application/SovereignBootWizard/SignatureParsing.c @@ -117,6 +117,7 @@ FillCertificateEntries ( UINT8 *TrustedCert; UINTN TrustedCertLength; UINTN CertCount; + EFI_GUID CertType; CertCount = 0; // @@ -131,7 +132,7 @@ FillCertificateEntries ( { SecDataDirLeft = SecDataDirEnd - Offset; if (SecDataDirLeft <= sizeof (WIN_CERTIFICATE)) { - DEBUG ((EFI_D_INFO, "Image Security Data Size too small\n")); + DEBUG ((DEBUG_ERROR, "Image Security Data Size too small\n")); break; } @@ -140,7 +141,7 @@ FillCertificateEntries ( (SecDataDirLeft - WinCertificate->dwLength < ALIGN_SIZE (WinCertificate->dwLength))) { - DEBUG ((EFI_D_INFO, "Image Security Data Size too small\n")); + DEBUG ((DEBUG_ERROR, "Image Security Data Size too small\n")); break; } @@ -154,7 +155,7 @@ FillCertificateEntries ( // PkcsCertData = (WIN_CERTIFICATE_EFI_PKCS *)WinCertificate; if (PkcsCertData->Hdr.dwLength <= sizeof (PkcsCertData->Hdr)) { - DEBUG ((EFI_D_INFO, "PKCS Cert Data too small\n")); + DEBUG ((DEBUG_ERROR, "PKCS Cert Data too small\n")); break; } @@ -166,12 +167,12 @@ FillCertificateEntries ( // WinCertUefiGuid = (WIN_CERTIFICATE_UEFI_GUID *)WinCertificate; if (WinCertUefiGuid->Hdr.dwLength <= OFFSET_OF (WIN_CERTIFICATE_UEFI_GUID, CertData)) { - DEBUG ((EFI_D_INFO, "WIN Cert UEFI Data too small\n")); + DEBUG ((DEBUG_ERROR, "WIN Cert UEFI Data too small\n")); break; } if (!CompareGuid (&WinCertUefiGuid->CertType, &gEfiCertPkcs7Guid)) { - DEBUG ((EFI_D_INFO, "Cert Type not PKCS7\n")); + DEBUG ((DEBUG_ERROR, "Cert Type not PKCS7\n")); continue; } @@ -179,18 +180,18 @@ FillCertificateEntries ( AuthDataSize = WinCertUefiGuid->Hdr.dwLength - OFFSET_OF (WIN_CERTIFICATE_UEFI_GUID, CertData); } else { if (WinCertificate->dwLength < sizeof (WIN_CERTIFICATE)) { - DEBUG ((EFI_D_INFO, "Unknown Cert Data too small\n")); + DEBUG ((DEBUG_ERROR, "Unknown Cert Data too small\n")); break; } - DEBUG ((EFI_D_INFO, "Unknown Cert Data, skipping\n")); + DEBUG ((DEBUG_INFO, "Unknown Cert Data, skipping\n")); continue; } NewCertEntry = (SV_CERT_ENTRY *) AllocateZeroPool (sizeof(SV_CERT_ENTRY)); if (NewCertEntry == NULL) { - DEBUG ((EFI_D_ERROR, "Not enough free memory for certificate data\n")); + DEBUG ((DEBUG_ERROR, "Not enough free memory for certificate data\n")); return; } @@ -201,12 +202,12 @@ FillCertificateEntries ( &CertBuffer, &BufferLength, &TrustedCert, &TrustedCertLength)) { FreePool (NewCertEntry); - DEBUG ((EFI_D_INFO, "Could not get PKCS7 signers\n")); + DEBUG ((DEBUG_ERROR, "Could not get PKCS7 signers\n")); continue; } if ((BufferLength == 0) || (CertBuffer == NULL) || ((*CertBuffer) == 0)) { FreePool (NewCertEntry); - DEBUG ((EFI_D_INFO, "PKCS7 signers data invalid\n")); + DEBUG ((DEBUG_ERROR, "PKCS7 signers data invalid\n")); continue; } @@ -219,8 +220,9 @@ FillCertificateEntries ( if (CalculateCertHash (TrustedCert, TrustedCertLength, HASHALG_SHA256, NewCertEntry->CertDigest)) { NewCertEntry->CertDigestSize = SHA256_DIGEST_SIZE; + CopyGuid (&NewCertEntry->CertType, &gEfiCertX509Sha256Guid); } else { - DEBUG ((EFI_D_INFO, "Could not calculate TBS certificate hash\n")); + DEBUG ((DEBUG_ERROR, "Could not calculate TBS certificate hash\n")); } HashStatus = HashPeImageByType ( @@ -230,7 +232,7 @@ FillCertificateEntries ( AuthDataSize, ImageDigest, &ImageDigestSize, - &NewCertEntry->CertType); + &CertType); if (EFI_ERROR (HashStatus)) { if (TrustedCert != NULL) { Pkcs7FreeSigners (TrustedCert); @@ -240,7 +242,7 @@ FillCertificateEntries ( continue; } - // Verify the digital signature amnd check against databases + // Verify the digital signature and check against databases NewCertEntry->CertIsInDb = IsAllowedByDb (AuthData, AuthDataSize, ImageDigest, ImageDigestSize); NewCertEntry->CertIsInDbx = IsForbiddenByDbx (AuthData, AuthDataSize, ImageDigest, ImageDigestSize); NewCertEntry->ImageIsVerified = AuthenticodeVerify ( @@ -249,13 +251,15 @@ FillCertificateEntries ( ImageDigest, ImageDigestSize); - DEBUG ((EFI_D_INFO, "Certificate Details:\n" - "\tImageIsVerified: %u\n" - "\tCertIsInDb: %u\n" - "\tCertIsInDbx: %u\n", + DEBUG ((DEBUG_INFO, "Certificate Details:\n" + " ImageIsVerified: %u\n" + " CertIsInDb: %u\n" + " CertIsInDbx: %u\n" + " CertIsMicrosoft: %u\n", NewCertEntry->ImageIsVerified, NewCertEntry->CertIsInDb, - NewCertEntry->CertIsInDbx)); + NewCertEntry->CertIsInDbx, + NewCertEntry->CertIsMicrosoft)); InsertTailList (&SecCtx->Certs, &NewCertEntry->CertLink); CertCount++; @@ -294,19 +298,17 @@ FillSecurityContext ( FullFilePath = NULL; LoadCtx = (SV_LOAD_CONTEXT *)Entry->VariableContext; - SecCtx = (SV_SECURITY_CONTEXT *)AllocateZeroPool(sizeof(SV_SECURITY_CONTEXT)); + SecCtx = (SV_SECURITY_CONTEXT *)AllocateZeroPool (sizeof(SV_SECURITY_CONTEXT)); Entry->SecurityContext = SecCtx; if (SecCtx == NULL) { - DEBUG ((EFI_D_INFO, "Not enough memory for security context\n")); + DEBUG ((DEBUG_ERROR, "Not enough memory for security context\n")); return EFI_OUT_OF_RESOURCES; } // Expand the Device PAth to the file if needed if (LoadCtx->NeedsPathExpansion) { FullFilePath = EfiBootManagerGetNextLoadOptionDevicePath (LoadCtx->FilePath, FullFilePath); - DEBUG ((EFI_D_INFO, "Expanded file path %s\n", - ConvertDevicePathToText(FullFilePath, FALSE, TRUE))); } else { FullFilePath = LoadCtx->FilePath; } @@ -326,13 +328,13 @@ FillSecurityContext ( } if (EFI_ERROR (Status)) { - DEBUG ((EFI_D_INFO, "Failed to read file: %r\n", Status)); + DEBUG ((DEBUG_ERROR, "Failed to read file: %r\n", Status)); goto ON_EXIT; } Status = GetImageSecDataDir (ImageBase, ImageSize, &SecDir); if (EFI_ERROR (Status)) { - DEBUG ((EFI_D_INFO, "Failed to get Image Security Data\n")); + DEBUG ((DEBUG_ERROR, "Failed to get Image Security Data\n")); goto ON_EXIT; } @@ -352,7 +354,7 @@ FillSecurityContext ( // No certificates, end here if ((SecDir == NULL) || (SecDir->Size == 0)) { - DEBUG ((EFI_D_INFO, "No Image Security Data, image is unsigned\n")); + DEBUG ((DEBUG_INFO, "No Image Security Data, image is unsigned\n")); SecCtx->ImageIsSigned = FALSE; SecCtx->NumCertificates = 0; goto ON_EXIT; @@ -366,7 +368,7 @@ FillSecurityContext ( Status = EFI_SUCCESS; ON_EXIT: - DEBUG ((EFI_D_INFO, "Image Details:\n" + DEBUG ((DEBUG_INFO, "Image Details:\n" " ImageIsInDbx: %u\n" " ImageIsInDb: %u\n" " ImageIsSigned: %u\n" @@ -475,8 +477,7 @@ ParseHashValue ( EFI_STATUS UpdateCertInfo ( IN SOVEREIGN_BOOT_WIZARD_PRIVATE_DATA *Private, - IN UINTN OptionNumber, - IN UINTN CertNumber + IN UINTN OptionNumber ) { SV_MENU_ENTRY *BootloaderEntry; @@ -501,8 +502,19 @@ UpdateCertInfo ( OldString = HiiGetString (Private->HiiHandle, STRING_TOKEN (STR_KEY_FINGERPRINT_HASH), NULL); + if (SecurityContext->ImageIsInDb || SecurityContext->ImageIsInDbx) { + DEBUG ((DEBUG_INFO, "Bootloader %u already (un)trusted\n", OptionNumber)); + // Free previous string if any + if (OldString != NULL) { + FreePool (OldString); + } + + return EFI_NO_MEDIA; + } + // Image is unsigned? Show its hash instead of certificates if (Private->FormData.ImageUnsigned) { + HiiSetString (Private->HiiHandle, STRING_TOKEN (STR_KEY_FINGERPRINT), L"Image hash (SHA-256):\n!!! Image is unsigned !!!", NULL); Status = ParseHashValue (SecurityContext->ImageDigest, SecurityContext->ImageDigestSize, &NewString); @@ -521,8 +533,38 @@ UpdateCertInfo ( } // Image is signed, show its certificate(s) - CertificateEntry = GetCertEntry(BootloaderEntry, CertNumber); - if (CertificateEntry == NULL) { + while (mCertIndex < SecurityContext->NumCertificates) { + CertificateEntry = GetCertEntry(BootloaderEntry, mCertIndex); + if (CertificateEntry == NULL) { + DEBUG ((DEBUG_ERROR, "Certificate %u not found\n", mCertIndex)); + // Free previous string if any + if (OldString != NULL) { + FreePool (OldString); + } + + return EFI_NO_MEDIA; + } + + // Do not show already trusted/utrusted or microsoft certificates + if (CertificateEntry->CertIsInDb || + CertificateEntry->CertIsInDbx || + CertificateEntry->CertIsMicrosoft) { + DEBUG ((DEBUG_INFO, "Certificate %u already (un)trusted or belongs to Microsoft\n", mCertIndex)); + mCertIndex++; + continue; + } + + break; + } + + // Haven't found any cert to show + if (mCertIndex >= SecurityContext->NumCertificates) { + DEBUG ((DEBUG_INFO, "Could not find a cerificate to show for bootloader %u\n", OptionNumber)); + // Free previous string if any + if (OldString != NULL) { + FreePool (OldString); + } + return EFI_NO_MEDIA; } diff --git a/DasharoModulePkg/Application/SovereignBootWizard/SovereignBootWizard.c b/DasharoModulePkg/Application/SovereignBootWizard/SovereignBootWizard.c index 12bcb93e27..f7a0bdf05a 100644 --- a/DasharoModulePkg/Application/SovereignBootWizard/SovereignBootWizard.c +++ b/DasharoModulePkg/Application/SovereignBootWizard/SovereignBootWizard.c @@ -8,6 +8,18 @@ SPDX-License-Identifier: BSD-2-Clause-Patent #include "SovereignBootWizard.h" +// The Sovereign Boot Wizard must be linked with Mbed TLS BaseCryptLib! +// The output of X509GetValidity can have different format depending on +// the library provider! +typedef struct { + INT32 Year; + INT32 Month; + INT32 Day; + INT32 Hour; + INT32 Minute; + INT32 Second; +} MBED_TLS_DATETIME_OBECT; + SOVEREIGN_BOOT_WIZARD_PRIVATE_DATA *mPrivateData = NULL; BOOLEAN mBootloadersInitted = FALSE; @@ -344,6 +356,275 @@ PrepareSbVariablesForSvBoot ( return SetSecureBootMode (STANDARD_SECURE_BOOT_MODE); } +/** + Helper function to populate an EFI_TIME instance. + + @param[in] Time FileContext cached in SecureBootConfig driver + +**/ +STATIC +EFI_STATUS +GetCurrentTime ( + IN EFI_TIME *Time + ) +{ + EFI_STATUS Status; + VOID *TestPointer; + + if (Time == NULL) { + return EFI_INVALID_PARAMETER; + } + + Status = gBS->LocateProtocol (&gEfiRealTimeClockArchProtocolGuid, NULL, &TestPointer); + if (EFI_ERROR (Status)) { + return Status; + } + + ZeroMem (Time, sizeof (EFI_TIME)); + Status = gRT->GetTime (Time, NULL); + if (EFI_ERROR (Status)) { + DEBUG (( + DEBUG_ERROR, + "%a(), GetTime() failed, status = '%r'\n", + __func__, + Status + )); + return Status; + } + + Time->Pad1 = 0; + Time->Nanosecond = 0; + Time->TimeZone = 0; + Time->Daylight = 0; + Time->Pad2 = 0; + + return EFI_SUCCESS; +} + +/** + Enroll a new hash into Signature Database (DB or DBX). + + @param[in] PrivateData The module's private data. + @param[in] VariableName Variable name of signature database, must be + EFI_IMAGE_SECURITY_DATABASE or EFI_IMAGE_SECURITY_DATABASE1. + + @retval EFI_SUCCESS New X509 is enrolled successfully. + @retval EFI_OUT_OF_RESOURCES Could not allocate needed resources. + +**/ +EFI_STATUS +EnrollHashToSigDB ( + IN SOVEREIGN_BOOT_WIZARD_PRIVATE_DATA *PrivateData, + IN CHAR16 *VariableName + ) +{ + EFI_SIGNATURE_LIST *SigDBHash; + EFI_SIGNATURE_DATA *SigDBHashData; + EFI_CERT_X509_SHA256 *SigDBCertHashData; + UINTN DataSize; + UINTN SigDBSize; + UINT32 Attr; + EFI_TIME Time; + SV_MENU_ENTRY *BootloaderEntry; + SV_SECURITY_CONTEXT *SecurityContext; + SV_CERT_ENTRY *CertificateEntry; + EFI_STATUS Status; + UINT8 CertValidFrom[64]; + UINTN CertValidFromLen; + UINT8 CertValidTo[64]; + UINTN CertValidToLen; + MBED_TLS_DATETIME_OBECT *CertValidToTime; + MBED_TLS_DATETIME_OBECT CurrentTime; + EFI_INPUT_KEY Key; + + BootloaderEntry = GetMenuEntry (&BootOptionMenu, mBootloaderIndex); + if (BootloaderEntry == NULL) { + return EFI_NO_MEDIA; + } + + SecurityContext = (SV_SECURITY_CONTEXT *)BootloaderEntry->SecurityContext; + if (SecurityContext == NULL) { + return EFI_NO_MEDIA; + } + + if (SecurityContext->ImageIsSigned) { + CertificateEntry = GetCertEntry (BootloaderEntry, mCertIndex); + if (CertificateEntry == NULL) { + return EFI_NO_MEDIA; + } + } + + DataSize = 0; + SigDBSize = 0; + SigDBHash = NULL; + SigDBHashData = NULL; + SigDBSize = sizeof (EFI_SIGNATURE_LIST) + sizeof (EFI_SIGNATURE_DATA) - 1; + + if (SecurityContext->ImageIsSigned) { + SigDBSize += sizeof (EFI_CERT_X509_SHA256); + } else { + SigDBSize += SHA256_DIGEST_SIZE; + } + + SigDBHash = (EFI_SIGNATURE_LIST *) AllocateZeroPool (SigDBSize); + if (SigDBHash == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto ON_EXIT; + } + + // + // Fill Certificate Database parameters. + // + SigDBHash->SignatureListSize = (UINT32)SigDBSize; + SigDBHash->SignatureHeaderSize = 0; + SigDBHash->SignatureSize = (UINT32)(SigDBSize - sizeof (EFI_SIGNATURE_LIST)); + + SigDBHashData = (EFI_SIGNATURE_DATA *)((UINT8 *)SigDBHash + sizeof (EFI_SIGNATURE_LIST)); + CopyGuid (&SigDBHashData->SignatureOwner, &gSovereignBootWizardFormSetGuid); + if (SecurityContext->ImageIsSigned) { + CopyGuid (&SigDBHash->SignatureType, &CertificateEntry->CertType); + SigDBCertHashData = (EFI_CERT_X509_SHA256 *)SigDBHashData->SignatureData; + CopyMem ((UINT8 *)(SigDBCertHashData->ToBeSignedHash), CertificateEntry->CertDigest, CertificateEntry->CertDigestSize); + + // If enrolling certificate hash to DBX, set to revoke always (keep revocation time 0). + // If enrolling to DB, set the revocation time to the expiry date + if (StrCmp(VariableName, EFI_IMAGE_SECURITY_DATABASE) == 0) { + CertValidFromLen = 64; + CertValidToLen = 64; + + if (!X509GetValidity(CertificateEntry->CertData, + CertificateEntry->CertDataSize, + CertValidFrom, + &CertValidFromLen, + CertValidTo, + &CertValidToLen)) { + DEBUG ((DEBUG_ERROR, "Could not get certificate validity\n")); + Status = EFI_NOT_FOUND; + goto ON_EXIT; + } + + if (CertValidToLen == 0 || CertValidToLen != sizeof (MBED_TLS_DATETIME_OBECT)) { + DEBUG ((DEBUG_ERROR, "Invalid certificate validity length\n")); + Status = EFI_BAD_BUFFER_SIZE; + goto ON_EXIT; + } + + CertValidToTime = (MBED_TLS_DATETIME_OBECT *)CertValidTo; + SigDBCertHashData->TimeOfRevocation.Year = (UINT16)(CertValidToTime->Year & 0xFFFF); + SigDBCertHashData->TimeOfRevocation.Month = (UINT8)(CertValidToTime->Month & 0xFF); + SigDBCertHashData->TimeOfRevocation.Day = (UINT8)(CertValidToTime->Day & 0xFF); + SigDBCertHashData->TimeOfRevocation.Hour = (UINT8)(CertValidToTime->Hour & 0xFF); + SigDBCertHashData->TimeOfRevocation.Minute = (UINT8)(CertValidToTime->Minute & 0xFF); + SigDBCertHashData->TimeOfRevocation.Second = (UINT8)(CertValidToTime->Second & 0xFF); + } + } else { + CopyGuid (&SigDBHash->SignatureType, &SecurityContext->HashType); + CopyMem ((UINT8 *)(SigDBHashData->SignatureData), SecurityContext->ImageDigest, SecurityContext->ImageDigestSize); + } + + // + // Check if signature database entry has been already populated. + // If true, use EFI_VARIABLE_APPEND_WRITE attribute to append the + // new signature data to original variable + // + Attr = EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_RUNTIME_ACCESS + | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS; + Status = GetCurrentTime (&Time); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "Fail to fetch valid time data: %r\n", Status)); + goto ON_EXIT; + } + + // Do not allow to add expired certificate to DB + if (SecurityContext->ImageIsSigned && + (StrCmp(VariableName, EFI_IMAGE_SECURITY_DATABASE) == 0)) { + CurrentTime.Year = (INT32)Time.Year; + CurrentTime.Month = (INT32)Time.Month; + CurrentTime.Day = (INT32)Time.Day; + CurrentTime.Hour = (INT32)Time.Hour; + CurrentTime.Minute = (INT32)Time.Minute; + CurrentTime.Second = (INT32)Time.Second; + + + if (X509CompareDateTime (&CurrentTime, CertValidToTime) >= 0) { + do { + CreatePopUp ( + EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE, + &Key, + L"", + L"The certificate you want to trust has expired.", + L"Can not add it to trusted signature database.", + L"", + L"Press ENTER to abort the process...", + L"", + NULL + ); + } while (Key.UnicodeChar != CHAR_CARRIAGE_RETURN); + + Status = EFI_SUCCESS; + goto ON_EXIT; + } + } + + Status = CreateTimeBasedPayload (&SigDBSize, (UINT8 **)&SigDBHash, &Time); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "Failed to create time-based data payload: %r\n", Status)); + goto ON_EXIT; + } + + Status = gRT->GetVariable ( + VariableName, + &gEfiImageSecurityDatabaseGuid, + NULL, + &DataSize, + NULL + ); + if (Status == EFI_BUFFER_TOO_SMALL) { + Attr |= EFI_VARIABLE_APPEND_WRITE; + } else if (Status != EFI_NOT_FOUND) { + DEBUG ((DEBUG_ERROR, "Failed to get %s variable size: %r\n", VariableName, Status)); + goto ON_EXIT; + } + + Status = SetSecureBootMode (CUSTOM_SECURE_BOOT_MODE); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "Failed to set custom Secure Boot mode: %r\n", Status)); + goto ON_EXIT; + } + + Status = gRT->SetVariable ( + VariableName, + &gEfiImageSecurityDatabaseGuid, + Attr, + SigDBSize, + SigDBHash + ); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "Failed to write the %s variable: %r\n", VariableName, Status)); + } else { + // If image is unsigned we have to increment the bootloader index to show + // next one. Otherwise move to next certificate. + if (!SecurityContext->ImageIsSigned) { + mBootloaderIndex++; + mCertIndex = 0; + DEBUG ((DEBUG_INFO, "Moving to next bootloader %u\n", mBootloaderIndex)); + } else { + mCertIndex++; + DEBUG ((DEBUG_INFO, "Moving to next certificate %u for bootloader %u\n", mCertIndex, mBootloaderIndex)); + } + } + + Status = SetSecureBootMode (STANDARD_SECURE_BOOT_MODE); + +ON_EXIT: + + if (SigDBHash != NULL) { + FreePool (SigDBHash); + } + + return Status; +} + EFI_STATUS AddKeyOrHashAsTrustedOrUntrusted ( SOVEREIGN_BOOT_WIZARD_PRIVATE_DATA *PrivateData, @@ -357,7 +638,8 @@ AddKeyOrHashAsTrustedOrUntrusted ( EFI_STRING PopupMessage; UINTN PopupMessageSize; EFI_STATUS Status; - + CHAR16 ErrorMessage[MAXIMUM_VALUE_CHARACTERS + 8]; + EFI_INPUT_KEY Key; Message = HiiGetString ( PrivateData->HiiHandle, @@ -388,9 +670,31 @@ AddKeyOrHashAsTrustedOrUntrusted ( if (UserSelection == EfiHiiPopupSelectionYes) { // Add key or hash to DB or DBX if (Trust) { - + Status = EnrollHashToSigDB (PrivateData, EFI_IMAGE_SECURITY_DATABASE); } else { + Status = EnrollHashToSigDB (PrivateData, EFI_IMAGE_SECURITY_DATABASE1); + } + if (EFI_ERROR (Status)) { + UnicodeSPrint ( + ErrorMessage, + (MAXIMUM_VALUE_CHARACTERS + 8) * sizeof (CHAR16), + L"Error: %r", + Status); + + CreatePopUp ( + EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE, + &Key, + L"", + Trust ? L"Could not add the certificate/image as trusted" : + L"Could not add the certificate/image as untrusted", + L"", + ErrorMessage, + L"", + L"Press any key to close this window...", + L"", + NULL + ); } } @@ -431,7 +735,7 @@ BootTheBootloader ( ); if (EFI_ERROR (Status)) { - DEBUG ((EFI_D_INFO, "Failed to prepare load option: %r\n", Status)); + DEBUG ((DEBUG_ERROR, "Failed to prepare load option: %r\n", Status)); return Status; } @@ -439,7 +743,7 @@ BootTheBootloader ( gST->ConOut->ClearScreen (gST->ConOut); } - DEBUG ((EFI_D_INFO, "Booting %s\n", BootloaderContext->Description)); + DEBUG ((DEBUG_INFO, "Booting %s\n", BootloaderContext->Description)); EfiBootManagerBoot (&BootOption); EfiBootManagerFreeLoadOption (&BootOption); @@ -673,12 +977,10 @@ Callback ( case DO_NOT_TRUST_KEY_FORM2_QUESTION_ID: if (!mBootloadersInitted) { Status = GetBootOptions (PrivateData); - DEBUG ((EFI_D_INFO, "GetBootOptions: %r\n", mBootloaderIndex, Status)); if (!EFI_ERROR (Status)) { mBootloadersInitted = TRUE; if (!mBootloadersShown) { Status = UpdateBootloaderPage (PrivateData); - DEBUG ((EFI_D_INFO, "UpdateBootloaderPage(%d): %r\n", mBootloaderIndex, Status)); mBootloadersShown = !EFI_ERROR (Status); } } else { diff --git a/DasharoModulePkg/Application/SovereignBootWizard/SovereignBootWizard.h b/DasharoModulePkg/Application/SovereignBootWizard/SovereignBootWizard.h index 2649295b1c..778bc3c3a1 100644 --- a/DasharoModulePkg/Application/SovereignBootWizard/SovereignBootWizard.h +++ b/DasharoModulePkg/Application/SovereignBootWizard/SovereignBootWizard.h @@ -161,7 +161,7 @@ typedef struct { BOOLEAN CertIsMicrosoft; // Placed exactly at offset 8 for alignment - UINT8 CertDigest[MAX_DIGEST_SIZE]; + UINT8 CertDigest[SHA256_DIGEST_SIZE]; UINTN CertDigestSize; UINTN CertDataSize; @@ -174,7 +174,7 @@ typedef struct { typedef struct { // Place first for 8 byte alignment - UINT8 ImageDigest[MAX_DIGEST_SIZE]; + UINT8 ImageDigest[SHA256_DIGEST_SIZE]; UINTN ImageDigestSize; BOOLEAN ImageIsInDbx; @@ -223,8 +223,7 @@ GetCertEntry ( EFI_STATUS UpdateCertInfo ( IN SOVEREIGN_BOOT_WIZARD_PRIVATE_DATA *Private, - IN UINTN OptionNumber, - IN UINTN CertNumber + IN UINTN OptionNumber ); #endif diff --git a/DasharoModulePkg/Application/SovereignBootWizard/SovereignBootWizard.inf b/DasharoModulePkg/Application/SovereignBootWizard/SovereignBootWizard.inf index 907b95e897..52ae9d3b6c 100644 --- a/DasharoModulePkg/Application/SovereignBootWizard/SovereignBootWizard.inf +++ b/DasharoModulePkg/Application/SovereignBootWizard/SovereignBootWizard.inf @@ -80,6 +80,7 @@ gEfiHiiPopupProtocolGuid ## CONSUMES gEfiDevicePathToTextProtocolGuid ## CONSUMES gEfiFirmwareVolume2ProtocolGuid ## CONSUMES + gEfiRealTimeClockArchProtocolGuid ## SOMETIMES_CONSUMES [UserExtensions.TianoCore."ExtraFiles"] SovereignBootWizardExtra.uni From 7f55d2975f3963c6e0a319965c66a6ebb3b1e258 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20=C5=BBygowski?= Date: Wed, 23 Jul 2025 17:52:02 +0200 Subject: [PATCH 09/28] DPP/OVMF: Use MBED TLS crypt library explicitly for SV Boot MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Michał Żygowski --- DasharoPayloadPkg/DasharoPayloadPkg.dsc | 5 ++++- OvmfPkg/OvmfPkgX64.dsc | 5 ++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/DasharoPayloadPkg/DasharoPayloadPkg.dsc b/DasharoPayloadPkg/DasharoPayloadPkg.dsc index e83826e42d..7471985d75 100644 --- a/DasharoPayloadPkg/DasharoPayloadPkg.dsc +++ b/DasharoPayloadPkg/DasharoPayloadPkg.dsc @@ -785,7 +785,10 @@ OrderedCollectionLib|MdePkg/Library/BaseOrderedCollectionRedBlackTreeLib/BaseOrd SecurityPkg/EnrollFromDefaultKeysApp/EnrollFromDefaultKeysApp.inf SecurityPkg/VariableAuthenticated/SecureBootDefaultKeysDxe/SecureBootDefaultKeysDxe.inf !if $(SOVEREIGN_BOOT_ENABLE) == TRUE - DasharoModulePkg/Application/SovereignBootWizard/SovereignBootWizard.inf + DasharoModulePkg/Application/SovereignBootWizard/SovereignBootWizard.inf { + + BaseCryptLib|CryptoPkg/Library/BaseCryptLibMbedTls/BaseCryptLib.inf + } !endif !endif diff --git a/OvmfPkg/OvmfPkgX64.dsc b/OvmfPkg/OvmfPkgX64.dsc index 49959e43c4..00fce76b64 100644 --- a/OvmfPkg/OvmfPkgX64.dsc +++ b/OvmfPkg/OvmfPkgX64.dsc @@ -1088,7 +1088,10 @@ OvmfPkg/EnrollDefaultKeys/EnrollDefaultKeys.inf !if $(SOVEREIGN_BOOT_ENABLE) == TRUE - DasharoModulePkg/Application/SovereignBootWizard/SovereignBootWizard.inf + DasharoModulePkg/Application/SovereignBootWizard/SovereignBootWizard.inf { + + BaseCryptLib|CryptoPkg/Library/BaseCryptLibMbedTls/BaseCryptLib.inf + } !endif !endif From de17948ee774ffe4305b29b246f520fe60c4e92e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20=C5=BBygowski?= Date: Thu, 24 Jul 2025 18:06:46 +0200 Subject: [PATCH 10/28] CryptoPkg/Include/Library/BaseCryptLib.h: Expose X509GetIssuerCommonName MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Michał Żygowski --- CryptoPkg/Include/Library/BaseCryptLib.h | 36 ++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/CryptoPkg/Include/Library/BaseCryptLib.h b/CryptoPkg/Include/Library/BaseCryptLib.h index 84ed87e05a..d8fe8ed429 100644 --- a/CryptoPkg/Include/Library/BaseCryptLib.h +++ b/CryptoPkg/Include/Library/BaseCryptLib.h @@ -1876,6 +1876,7 @@ X509GetCommonName ( IN OUT UINTN *CommonNameSize ); + /** Retrieve the organization name (O) string from one X.509 certificate. @@ -2675,6 +2676,41 @@ X509GetIssuerName ( IN OUT UINTN *CertIssuerSize ); +/** + Retrieve the issuer common name (CN) string from one X.509 certificate. + + @param[in] Cert Pointer to the DER-encoded X509 certificate. + @param[in] CertSize Size of the X509 certificate in bytes. + @param[out] CommonName Buffer to contain the retrieved certificate issuer common + name string. At most CommonNameSize bytes will be + written and the string will be null terminated. May be + NULL in order to determine the size buffer needed. + @param[in,out] CommonNameSize The size in bytes of the CommonName buffer on input, + and the size of buffer returned CommonName on output. + If CommonName is NULL then the amount of space needed + in buffer (including the final null) is returned. + + @retval RETURN_SUCCESS The certificate Issuer CommonName retrieved successfully. + @retval RETURN_INVALID_PARAMETER If Cert is NULL. + If CommonNameSize is NULL. + If CommonName is not NULL and *CommonNameSize is 0. + If Certificate is invalid. + @retval RETURN_NOT_FOUND If no CommonName entry exists. + @retval RETURN_BUFFER_TOO_SMALL If the CommonName is NULL. The required buffer size + (including the final null) is returned in the + CommonNameSize parameter. + @retval RETURN_UNSUPPORTED The operation is not supported. + +**/ +RETURN_STATUS +EFIAPI +X509GetIssuerCommonName ( + IN CONST UINT8 *Cert, + IN UINTN CertSize, + OUT CHAR8 *CommonName OPTIONAL, + IN OUT UINTN *CommonNameSize + ); + /** Retrieve the Signature Algorithm from one X.509 certificate. From dc1a4cd61c05c90a046b4f479c0013a63f54b1d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20=C5=BBygowski?= Date: Thu, 24 Jul 2025 18:14:11 +0200 Subject: [PATCH 11/28] DasharoModulePkg/Application/SovereignBootWizard: Add certificate details form MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Michał Żygowski --- .../SovereignBootWizard/SignatureParsing.c | 340 ++++++++++++++++-- .../SovereignBootWizard/SovereignBootWizard.c | 19 +- .../SovereignBootWizard/SovereignBootWizard.h | 17 + .../SovereignBootWizardHii.h | 1 + .../SovereignBootWizardVfr.vfr | 57 ++- .../SovereignBootWizardVfrStrings.uni | 25 +- 6 files changed, 410 insertions(+), 49 deletions(-) diff --git a/DasharoModulePkg/Application/SovereignBootWizard/SignatureParsing.c b/DasharoModulePkg/Application/SovereignBootWizard/SignatureParsing.c index b160dc07d4..a53fba8265 100644 --- a/DasharoModulePkg/Application/SovereignBootWizard/SignatureParsing.c +++ b/DasharoModulePkg/Application/SovereignBootWizard/SignatureParsing.c @@ -431,7 +431,7 @@ GetCertEntry ( } /** - Parse hash value from EFI_SIGNATURE_DATA, and save in the CHAR16 type array. + Parse hash value from buffer, and save in the CHAR16 type array. The buffer is callee allocated and should be freed by the caller. @param[in] Digest The pointer to the hash value. @@ -461,19 +461,91 @@ ParseHashValue ( // Each byte will split two Hex-number. // TotalSize = (DigestSize * 2 * sizeof (CHAR16)); + // One extra byte for NULL termination + TotalSize += sizeof (CHAR16); *BufferToReturn = AllocateZeroPool (TotalSize); if (*BufferToReturn == NULL) { return EFI_OUT_OF_RESOURCES; } - for (Index = 0, BufferIndex = 0; Index < DigestSize; Index = Index + 1) { + for (Index = 0, BufferIndex = 0; Index < DigestSize; Index++) { BufferIndex += UnicodeSPrint (&(*BufferToReturn)[BufferIndex], TotalSize - sizeof (CHAR16) * BufferIndex, L"%02x", Digest[Index]); } return EFI_SUCCESS; } +/** + Parse key modulus from buffer, and save in the CHAR16 type array. + The buffer is callee allocated and should be freed by the caller. + + @param[in] Digest The pointer to the hash value. + @param[in] DigestSize The size of the hash. + @param[out] BufferToReturn Buffer to save the hash value. + + @retval EFI_INVALID_PARAMETER Invalid Hash or Buffer. + @retval EFI_OUT_OF_RESOURCES A memory allocation failed. + @retval EFI_SUCCESS Operation success. +**/ +EFI_STATUS +ParseKeyModulus ( + IN UINT8 *Digest, + IN UINTN DigestSize, + OUT CHAR16 **BufferToReturn + ) +{ + UINTN Index; + UINTN BufferIndex; + UINTN TotalSize; + UINTN BytesPerLine; + UINTN Line; + + if ((Digest == NULL) || (DigestSize == 0) || (BufferToReturn == NULL)) { + return EFI_INVALID_PARAMETER; + } + + BufferIndex = 0; + BytesPerLine = 16; + Line = ((DigestSize + BytesPerLine - 1) / BytesPerLine); + + // + // Each byte will be split into two hex numbers + colon delimiter. Each line + // will start with 4 spaces and end with newline (/r/n) so need another 6 + // chars per line. + // + TotalSize = ((DigestSize * 3) + (Line * 6)) * sizeof (CHAR16); + // One extra byte for NULL termination + TotalSize += sizeof (CHAR16); + + *BufferToReturn = AllocateZeroPool (TotalSize); + if (*BufferToReturn == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + BufferIndex += UnicodeSPrint ( + &(*BufferToReturn)[BufferIndex], + TotalSize - sizeof (CHAR16) * BufferIndex, + L" "); + for (Index = 0; Index < DigestSize; Index++) { + if ((Index > 0) && (Index % BytesPerLine == 0)) { + BufferIndex += UnicodeSPrint ( + &(*BufferToReturn)[BufferIndex], + TotalSize - sizeof (CHAR16) * BufferIndex, + L"\n "); + } + + BufferIndex += UnicodeSPrint ( + &(*BufferToReturn)[BufferIndex], + TotalSize - sizeof (CHAR16) * BufferIndex, + Index == (DigestSize - 1) ? L"%02x" : L"%02x:", + Digest[Index]); + + } + + return EFI_SUCCESS; +} + EFI_STATUS UpdateCertInfo ( IN SOVEREIGN_BOOT_WIZARD_PRIVATE_DATA *Private, @@ -484,7 +556,6 @@ UpdateCertInfo ( SV_CERT_ENTRY *CertificateEntry; SV_SECURITY_CONTEXT *SecurityContext; EFI_STRING NewString; - EFI_STRING OldString; EFI_STATUS Status; BootloaderEntry = GetMenuEntry (&BootOptionMenu, OptionNumber); @@ -500,15 +571,8 @@ UpdateCertInfo ( Private->FormData.ImageUnsigned = (!SecurityContext->ImageIsSigned || (SecurityContext->NumCertificates == 0)); - OldString = HiiGetString (Private->HiiHandle, STRING_TOKEN (STR_KEY_FINGERPRINT_HASH), NULL); - if (SecurityContext->ImageIsInDb || SecurityContext->ImageIsInDbx) { DEBUG ((DEBUG_INFO, "Bootloader %u already (un)trusted\n", OptionNumber)); - // Free previous string if any - if (OldString != NULL) { - FreePool (OldString); - } - return EFI_NO_MEDIA; } @@ -518,17 +582,13 @@ UpdateCertInfo ( HiiSetString (Private->HiiHandle, STRING_TOKEN (STR_KEY_FINGERPRINT), L"Image hash (SHA-256):\n!!! Image is unsigned !!!", NULL); Status = ParseHashValue (SecurityContext->ImageDigest, SecurityContext->ImageDigestSize, &NewString); - if (!EFI_ERROR (Status) && (NewString != NULL)) { + if (!EFI_ERROR (Status)) { HiiSetString (Private->HiiHandle, STRING_TOKEN (STR_KEY_FINGERPRINT_HASH), NewString, NULL); + FreePool (NewString); } else { HiiSetString (Private->HiiHandle, STRING_TOKEN (STR_KEY_FINGERPRINT_HASH), L"Image hash could not be obtained.", NULL); } - // Free previous string if any - if (OldString != NULL) { - FreePool (OldString); - } - return EFI_SUCCESS; } @@ -537,11 +597,6 @@ UpdateCertInfo ( CertificateEntry = GetCertEntry(BootloaderEntry, mCertIndex); if (CertificateEntry == NULL) { DEBUG ((DEBUG_ERROR, "Certificate %u not found\n", mCertIndex)); - // Free previous string if any - if (OldString != NULL) { - FreePool (OldString); - } - return EFI_NO_MEDIA; } @@ -560,29 +615,252 @@ UpdateCertInfo ( // Haven't found any cert to show if (mCertIndex >= SecurityContext->NumCertificates) { DEBUG ((DEBUG_INFO, "Could not find a cerificate to show for bootloader %u\n", OptionNumber)); - // Free previous string if any - if (OldString != NULL) { - FreePool (OldString); - } - return EFI_NO_MEDIA; } HiiSetString (Private->HiiHandle, STRING_TOKEN (STR_KEY_FINGERPRINT), L"Certificate fingerprint (SHA-256):", NULL); Status = ParseHashValue (CertificateEntry->CertDigest, CertificateEntry->CertDigestSize, &NewString); - if (!EFI_ERROR (Status) && (NewString != NULL)) { + if (!EFI_ERROR (Status)) { HiiSetString (Private->HiiHandle, STRING_TOKEN (STR_KEY_FINGERPRINT_HASH), NewString, NULL); + FreePool (NewString); } else { HiiSetString (Private->HiiHandle, STRING_TOKEN (STR_KEY_FINGERPRINT_HASH), L"Could not obtain certificate fingerprint", NULL); } - // Free previous string if any - if (OldString != NULL) { - FreePool (OldString); + return EFI_SUCCESS; +} + +VOID +UpdateCertValidtyStrings ( + IN SOVEREIGN_BOOT_WIZARD_PRIVATE_DATA *Private, + IN SV_CERT_ENTRY *CertificateEntry + ) +{ + CHAR16 DateBuffer[30]; + UINT8 CertValidFrom[64]; + UINTN CertValidFromLen; + UINT8 CertValidTo[64]; + UINTN CertValidToLen; + MBED_TLS_DATETIME_OBECT *CertValidTime; + + CertValidFromLen = 64; + CertValidToLen = 64; + + if (X509GetValidity(CertificateEntry->CertData, + CertificateEntry->CertDataSize, + CertValidFrom, + &CertValidFromLen, + CertValidTo, + &CertValidToLen)) { + + CertValidTime = (MBED_TLS_DATETIME_OBECT *)CertValidTo; + SetMem (DateBuffer, sizeof (DateBuffer), 0); + UnicodeSPrint ( + DateBuffer, + sizeof (DateBuffer), + L"%04u-%02u-%02u %02u:%02u:%02u", + (UINT16)(CertValidTime->Year & 0xFFFF), + (UINT8)(CertValidTime->Month & 0xFF), + (UINT8)(CertValidTime->Day & 0xFF), + (UINT8)(CertValidTime->Hour & 0xFF), + (UINT8)(CertValidTime->Minute & 0xFF), + (UINT8)(CertValidTime->Second & 0xFF)); + HiiSetString (Private->HiiHandle, STRING_TOKEN (STR_VALIDITY_AFTER_DATE), DateBuffer, NULL); + + CertValidTime = (MBED_TLS_DATETIME_OBECT *)CertValidFrom; + SetMem (DateBuffer, sizeof (DateBuffer), 0); + UnicodeSPrint ( + DateBuffer, + sizeof (DateBuffer), + L"%04u-%02u-%02u %02u:%02u:%02u", + (UINT16)(CertValidTime->Year & 0xFFFF), + (UINT8)(CertValidTime->Month & 0xFF), + (UINT8)(CertValidTime->Day & 0xFF), + (UINT8)(CertValidTime->Hour & 0xFF), + (UINT8)(CertValidTime->Minute & 0xFF), + (UINT8)(CertValidTime->Second & 0xFF)); + + HiiSetString (Private->HiiHandle, STRING_TOKEN (STR_VALIDITY_BEFORE_DATE), DateBuffer, NULL); + + } else { + HiiSetString (Private->HiiHandle, STRING_TOKEN (STR_VALIDITY_AFTER_DATE), L"Unknown", NULL); + HiiSetString (Private->HiiHandle, STRING_TOKEN (STR_VALIDITY_BEFORE_DATE), L"Unknown", NULL); + } +} + +VOID +UpdateCertIssuerAndSubjectStrings ( + IN SOVEREIGN_BOOT_WIZARD_PRIVATE_DATA *Private, + IN SV_CERT_ENTRY *CertificateEntry + ) +{ + CHAR8 StringBuffer[500]; + UINTN StringBufferSize; + CHAR16 *NewString; + + HiiSetString (Private->HiiHandle, STRING_TOKEN (STR_CERT_ISSUER2), L"Unknown", NULL); + HiiSetString (Private->HiiHandle, STRING_TOKEN (STR_CERT_SUBJECT2), L"Unknown", NULL); + + StringBufferSize = sizeof (StringBuffer); + SetMem (StringBuffer, StringBufferSize, 0); + if (!RETURN_ERROR (X509GetIssuerCommonName (CertificateEntry->CertData, + CertificateEntry->CertDataSize, + StringBuffer, + &StringBufferSize))) { + + NewString = (CHAR16 *)AllocateZeroPool ((StringBufferSize + 1) * sizeof(CHAR16)); + if (NewString != NULL) { + if (!RETURN_ERROR (AsciiStrToUnicodeStrS (StringBuffer, NewString, StringBufferSize + 1))) { + HiiSetString (Private->HiiHandle, STRING_TOKEN (STR_CERT_ISSUER2), NewString, NULL); + } + FreePool (NewString); + } + } else { + DEBUG ((DEBUG_INFO, "Could not get Issuer name\n")); + } + + StringBufferSize = sizeof (StringBuffer); + SetMem (StringBuffer, StringBufferSize, 0); + if (!RETURN_ERROR (X509GetCommonName (CertificateEntry->CertData, + CertificateEntry->CertDataSize, + StringBuffer, + &StringBufferSize))) { + + NewString = (CHAR16 *)AllocateZeroPool ((StringBufferSize + 1) * sizeof(CHAR16)); + if (NewString != NULL) { + if (!RETURN_ERROR (AsciiStrToUnicodeStrS (StringBuffer, NewString, StringBufferSize + 1))) { + HiiSetString (Private->HiiHandle, STRING_TOKEN (STR_CERT_SUBJECT2), NewString, NULL); + } + FreePool (NewString); + } + } else { + DEBUG ((DEBUG_INFO, "Could not get Subject name\n")); + } +} + +VOID +UpdateCertSerialNumberString ( + IN SOVEREIGN_BOOT_WIZARD_PRIVATE_DATA *Private, + IN SV_CERT_ENTRY *CertificateEntry + ) +{ + UINT8 StringBuffer[200]; + UINTN StringBufferSize; + CHAR16 *NewString; + + HiiSetString (Private->HiiHandle, STRING_TOKEN (STR_CERT_SERIAL_NUMBER2), L"Unknown", NULL); + + StringBufferSize = sizeof (StringBuffer); + SetMem (StringBuffer, StringBufferSize, 0); + if (X509GetSerialNumber(CertificateEntry->CertData, + CertificateEntry->CertDataSize, + StringBuffer, + &StringBufferSize)) { + + // Serial number is NULL terminated. But this NULL termination is not a + // part of the serial number itself, thus we should parse one byte less. + if (!EFI_ERROR (ParseHashValue (StringBuffer, StringBufferSize - 1, &NewString))) { + HiiSetString (Private->HiiHandle, STRING_TOKEN (STR_CERT_SERIAL_NUMBER2), NewString, NULL); + FreePool (NewString); + } + } +} + +VOID +UpdateCertKeyStrings ( + IN SOVEREIGN_BOOT_WIZARD_PRIVATE_DATA *Private, + IN SV_CERT_ENTRY *CertificateEntry + ) +{ + UINT8 *ModulusBuffer; + CHAR16 *NewString; + UINT32 Exponent; + UINT16 ExponentString[20]; + VOID *X509PubKey; + UINTN PubKeyModSize; + UINTN PubKeyExpSize; + + HiiSetString (Private->HiiHandle, STRING_TOKEN (STR_CERT_KEY_MODULUS_HEX), L"Unknown", NULL); + HiiSetString (Private->HiiHandle, STRING_TOKEN (STR_CERT_KEY_EXPONENT2), L"Unknown", NULL); + + if (!RsaGetPublicKeyFromX509 (CertificateEntry->CertData, CertificateEntry->CertDataSize, &X509PubKey)) { + DEBUG ((DEBUG_ERROR, "Error occurred while parsing the pubkey from certificate.\n")); + return; + } + + if (X509PubKey == NULL) { + DEBUG ((DEBUG_ERROR, "X509 key is NULL\n")); + return; + } + + Exponent = 0; + PubKeyModSize = 0; + PubKeyExpSize = 0; + + RsaGetKey (X509PubKey, RsaKeyN, NULL, &PubKeyModSize); + RsaGetKey (X509PubKey, RsaKeyE, NULL, &PubKeyExpSize); + + if (PubKeyExpSize > 4) { + DEBUG ((DEBUG_ERROR, "Key exponent size too big\n")); + RsaFree (X509PubKey); + return; + } + + ModulusBuffer = (UINT8 *) AllocateZeroPool (PubKeyModSize); + if (ModulusBuffer == NULL) { + DEBUG ((DEBUG_ERROR, "Could not allocate memory for modulus\n")); + RsaFree (X509PubKey); + return; + } + + if (!RsaGetKey (X509PubKey, RsaKeyN, ModulusBuffer, &PubKeyModSize)) { + DEBUG ((DEBUG_ERROR, "Could not get key modulus\n")); + goto ON_EXIT; + } + + if (!RsaGetKey (X509PubKey, RsaKeyE, (UINT8 *)&Exponent, &PubKeyExpSize)) { + DEBUG ((DEBUG_ERROR, "Could not get key exponent\n")); + goto ON_EXIT; + } + + if (!EFI_ERROR (ParseKeyModulus (ModulusBuffer, PubKeyModSize, &NewString))) { + HiiSetString (Private->HiiHandle, STRING_TOKEN (STR_CERT_KEY_MODULUS_HEX), NewString, NULL); + FreePool (NewString); + } + + SetMem(ExponentString, sizeof (ExponentString), 0); + UnicodeSPrint(ExponentString, sizeof (ExponentString), L"0x%X", Exponent); + HiiSetString (Private->HiiHandle, STRING_TOKEN (STR_CERT_KEY_EXPONENT2), ExponentString, NULL); + + +ON_EXIT: + RsaFree (X509PubKey); + FreePool (ModulusBuffer); +} + +EFI_STATUS +UpdateCertDetails ( + IN SOVEREIGN_BOOT_WIZARD_PRIVATE_DATA *Private + ) +{ + SV_MENU_ENTRY *BootloaderEntry; + SV_CERT_ENTRY *CertificateEntry; + + BootloaderEntry = GetMenuEntry (&BootOptionMenu, mBootloaderIndex); + if (BootloaderEntry == NULL) { + return EFI_NO_MEDIA; + } + + CertificateEntry = GetCertEntry(BootloaderEntry, mCertIndex); + if (CertificateEntry == NULL) { + return EFI_NO_MEDIA; } - // TODO key details + UpdateCertValidtyStrings (Private, CertificateEntry); + UpdateCertIssuerAndSubjectStrings (Private, CertificateEntry); + UpdateCertSerialNumberString (Private, CertificateEntry); + UpdateCertKeyStrings (Private, CertificateEntry); return EFI_SUCCESS; } diff --git a/DasharoModulePkg/Application/SovereignBootWizard/SovereignBootWizard.c b/DasharoModulePkg/Application/SovereignBootWizard/SovereignBootWizard.c index f7a0bdf05a..89573b566b 100644 --- a/DasharoModulePkg/Application/SovereignBootWizard/SovereignBootWizard.c +++ b/DasharoModulePkg/Application/SovereignBootWizard/SovereignBootWizard.c @@ -8,18 +8,6 @@ SPDX-License-Identifier: BSD-2-Clause-Patent #include "SovereignBootWizard.h" -// The Sovereign Boot Wizard must be linked with Mbed TLS BaseCryptLib! -// The output of X509GetValidity can have different format depending on -// the library provider! -typedef struct { - INT32 Year; - INT32 Month; - INT32 Day; - INT32 Hour; - INT32 Minute; - INT32 Second; -} MBED_TLS_DATETIME_OBECT; - SOVEREIGN_BOOT_WIZARD_PRIVATE_DATA *mPrivateData = NULL; BOOLEAN mBootloadersInitted = FALSE; @@ -870,6 +858,12 @@ Callback ( Status = AddKeyOrHashAsTrustedOrUntrusted(PrivateData, TRUE); break; } + case SHOW_KEY_DETAILS_FORM2_QUESTION_ID: + { + // Update the strings when opening certificate details form + Status = UpdateCertDetails (PrivateData); + break; + } default: break; } @@ -1009,7 +1003,6 @@ Callback ( } } break; - default: break; } diff --git a/DasharoModulePkg/Application/SovereignBootWizard/SovereignBootWizard.h b/DasharoModulePkg/Application/SovereignBootWizard/SovereignBootWizard.h index 778bc3c3a1..2799fcc5ad 100644 --- a/DasharoModulePkg/Application/SovereignBootWizard/SovereignBootWizard.h +++ b/DasharoModulePkg/Application/SovereignBootWizard/SovereignBootWizard.h @@ -116,6 +116,18 @@ typedef struct { #pragma pack() +// The Sovereign Boot Wizard must be linked with Mbed TLS BaseCryptLib! +// The output of X509GetValidity can have different format depending on +// the library provider! +typedef struct { + INT32 Year; + INT32 Month; + INT32 Day; + INT32 Hour; + INT32 Minute; + INT32 Second; +} MBED_TLS_DATETIME_OBECT; + typedef struct { UINTN Signature; LIST_ENTRY Head; @@ -226,4 +238,9 @@ UpdateCertInfo ( IN UINTN OptionNumber ); +EFI_STATUS +UpdateCertDetails ( + IN SOVEREIGN_BOOT_WIZARD_PRIVATE_DATA *Private + ); + #endif diff --git a/DasharoModulePkg/Application/SovereignBootWizard/SovereignBootWizardHii.h b/DasharoModulePkg/Application/SovereignBootWizard/SovereignBootWizardHii.h index c5597e0390..315b7f0ff3 100644 --- a/DasharoModulePkg/Application/SovereignBootWizard/SovereignBootWizardHii.h +++ b/DasharoModulePkg/Application/SovereignBootWizard/SovereignBootWizardHii.h @@ -29,6 +29,7 @@ Revision History: #define SOVEREIGN_BOOT_WIZARD_WELCOME_FORM_ID 1 #define SOVEREIGN_BOOT_WIZARD_CONFIG_FORM_ID 2 #define SOVEREIGN_BOOT_WIZARD_MS_SECURE_BOOT_FORM_ID 3 +#define SOVEREIGN_BOOT_WIZARD_KEY_DETAILS_FORM_ID 4 #define SOVEREIGN_BOOT_WIZARD_INTERACTIVE_MODE_FORM_ID 9 // Question IDs diff --git a/DasharoModulePkg/Application/SovereignBootWizard/SovereignBootWizardVfr.vfr b/DasharoModulePkg/Application/SovereignBootWizard/SovereignBootWizardVfr.vfr index d74d2e8dec..d6e74b1c57 100644 --- a/DasharoModulePkg/Application/SovereignBootWizard/SovereignBootWizardVfr.vfr +++ b/DasharoModulePkg/Application/SovereignBootWizard/SovereignBootWizardVfr.vfr @@ -135,12 +135,16 @@ formset key = TRUST_KEY_FORM2_QUESTION_ID; endif; - grayoutif ideqval SvBootFormData.ImageUnsigned == 1; - text + subtitle text = STRING_TOKEN(STR_EMPTY_STRING); + + suppressif ideqval SvBootFormData.ImageUnsigned == 1; + goto SOVEREIGN_BOOT_WIZARD_KEY_DETAILS_FORM_ID, + prompt = STRING_TOKEN(STR_SHOW_KEY_DETAILS), help = STRING_TOKEN(STR_EMPTY_STRING), - text = STRING_TOKEN(STR_SHOW_KEY_DETAILS), flags = INTERACTIVE, key = SHOW_KEY_DETAILS_FORM2_QUESTION_ID; + + subtitle text = STRING_TOKEN(STR_EMPTY_STRING); endif; text @@ -159,6 +163,53 @@ formset title = STRING_TOKEN(STR_FORM1_TITLE); endform; + + form formid = SOVEREIGN_BOOT_WIZARD_KEY_DETAILS_FORM_ID, + title = STRING_TOKEN(STR_FORM4_TITLE); + + grayoutif TRUE; + text + help = STRING_TOKEN(STR_EMPTY_STRING), + text = STRING_TOKEN(STR_CERT_SERIAL_NUMBER), + text = STRING_TOKEN(STR_CERT_SERIAL_NUMBER2); + + text + help = STRING_TOKEN(STR_EMPTY_STRING), + text = STRING_TOKEN(STR_CERT_ISSUER), + text = STRING_TOKEN(STR_CERT_ISSUER2); + + text + help = STRING_TOKEN(STR_EMPTY_STRING), + text = STRING_TOKEN(STR_VALIDITY_BEFORE), + text = STRING_TOKEN(STR_VALIDITY_BEFORE_DATE); + + text + help = STRING_TOKEN(STR_EMPTY_STRING), + text = STRING_TOKEN(STR_VALIDITY_AFTER), + text = STRING_TOKEN(STR_VALIDITY_AFTER_DATE); + + text + help = STRING_TOKEN(STR_EMPTY_STRING), + text = STRING_TOKEN(STR_CERT_SUBJECT), + text = STRING_TOKEN(STR_CERT_SUBJECT2); + + text + help = STRING_TOKEN(STR_EMPTY_STRING), + text = STRING_TOKEN(STR_CERT_KEY_MODULUS); + + text + help = STRING_TOKEN(STR_EMPTY_STRING), + text = STRING_TOKEN(STR_CERT_KEY_MODULUS_HEX); + + text + help = STRING_TOKEN(STR_EMPTY_STRING), + text = STRING_TOKEN(STR_CERT_KEY_EXPONENT), + text = STRING_TOKEN(STR_CERT_KEY_EXPONENT2); + + endif; + endform; + + // // Advanced mode window // diff --git a/DasharoModulePkg/Application/SovereignBootWizard/SovereignBootWizardVfrStrings.uni b/DasharoModulePkg/Application/SovereignBootWizard/SovereignBootWizardVfrStrings.uni index d82acea059..ad7f924685 100644 --- a/DasharoModulePkg/Application/SovereignBootWizard/SovereignBootWizardVfrStrings.uni +++ b/DasharoModulePkg/Application/SovereignBootWizard/SovereignBootWizardVfrStrings.uni @@ -65,7 +65,7 @@ #string STR_KEY_FINGERPRINT_HASH #language en-US "" #string STR_TRUST_QUESTION #language en-US "Do you want to trust this key/image and continue booting?" -#string STR_SV_TRUST_KEY_POPUP #language en-US " " // will be updated dynamically with a hash +#string STR_SV_TRUST_KEY_POPUP #language en-US "" #string STR_SV_TRUST_KEY_QUESTION #language en-US "Are you sure you want to trust the following" #string STR_SV_UNTRUST_KEY_QUESTION #language en-US "Are you sure you do NOT want to trust the following" @@ -73,7 +73,28 @@ #string STR_DO_NOT_TRUST_KEY2 #language en-US "[ Do NOT trust ]" #string STR_TRUST_KEY_AND_BOOT #language en-US "[ Trust this key/image and boot ]" #string STR_TRUST_KEY #language en-US "[ Trust this key/image, next key/bootloader ]" -#string STR_SHOW_KEY_DETAILS #language en-US "[ Show key/certificate details ]" +#string STR_SHOW_KEY_DETAILS #language en-US "Show key/certificate details" + +// Configuration page strings +#string STR_FORM4_TITLE #language en-US "Certificate Details" + +#string STR_VALIDITY_BEFORE #language en-US "Valid Not Before:" +#string STR_VALIDITY_AFTER #language en-US "Valid Not After:" +#string STR_VALIDITY_BEFORE_DATE #language en-US "" +#string STR_VALIDITY_AFTER_DATE #language en-US "" + +#string STR_CERT_ISSUER #language en-US "Issuer Common Name (CN):" +#string STR_CERT_ISSUER2 #language en-US "" +#string STR_CERT_SUBJECT #language en-US "Subject Common Name (CN):" +#string STR_CERT_SUBJECT2 #language en-US "" + +#string STR_CERT_SERIAL_NUMBER #language en-US "Serial Number (HEX):" +#string STR_CERT_SERIAL_NUMBER2 #language en-US "" + +#string STR_CERT_KEY_MODULUS #language en-US "Key Modulus:" +#string STR_CERT_KEY_MODULUS_HEX #language en-US "" +#string STR_CERT_KEY_EXPONENT #language en-US "Key Exponent:" +#string STR_CERT_KEY_EXPONENT2 #language en-US "" // Interactive mode strings #string STR_FORM9_TITLE #language en-US "Sovereign Boot Provisioning Wizard" From 4a26e9ce933dd6bbe4920efeafa1e1691393a5e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20=C5=BBygowski?= Date: Fri, 25 Jul 2025 10:22:04 +0200 Subject: [PATCH 12/28] DMP/Application/SovereignBootWizard: Move SB key management to separate file MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Michał Żygowski --- .../SovereignBootWizard/KeyManagement.c | 488 ++++++++++++++++++ .../SovereignBootWizard/SovereignBootWizard.c | 480 ----------------- .../SovereignBootWizard/SovereignBootWizard.h | 16 + .../SovereignBootWizard.inf | 1 + 4 files changed, 505 insertions(+), 480 deletions(-) create mode 100644 DasharoModulePkg/Application/SovereignBootWizard/KeyManagement.c diff --git a/DasharoModulePkg/Application/SovereignBootWizard/KeyManagement.c b/DasharoModulePkg/Application/SovereignBootWizard/KeyManagement.c new file mode 100644 index 0000000000..08f7a4834b --- /dev/null +++ b/DasharoModulePkg/Application/SovereignBootWizard/KeyManagement.c @@ -0,0 +1,488 @@ +/** @file +Sovereign Boot Wizard implementation. + +Copyright (c) 2025, 3mdeb Sp z o.o. All rights reserved.
+SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "SovereignBootWizard.h" + +STATIC EFI_STATUS +DeleteAllSecureBootVariables ( + VOID + ) +{ + EFI_STATUS Status, TempStatus; + + Status = DeletePlatformKey (); + DEBUG ((DEBUG_INFO, "%a - PK Delete = %r\n", __func__, Status)); + // If the PK is not found, then our work here is done. + if (Status == EFI_NOT_FOUND) { + Status = EFI_SUCCESS; + } + // If any other error occurred, let's inform the caller that the PK delete in particular failed. + else if (EFI_ERROR (Status)) { + Status = EFI_ABORTED; + } + + // + // If any of THESE steps have an error, report the error but attempt to delete all keys. + // Using TempStatus will prevent an error from being trampled by an EFI_SUCCESS. + // Overwrite Status ONLY if TempStatus is an error. + // + // If the error is EFI_NOT_FOUND, we can safely ignore it since we were trying to delete + // the variables anyway. + // + TempStatus = DeleteKEK (); + DEBUG ((DEBUG_INFO, "%a - KEK Delete = %r\n", __func__, TempStatus)); + if (EFI_ERROR (TempStatus) && (TempStatus != EFI_NOT_FOUND)) { + Status = EFI_ACCESS_DENIED; + } + + TempStatus = DeleteDb (); + DEBUG ((DEBUG_INFO, "%a - db Delete = %r\n", __func__, TempStatus)); + if (EFI_ERROR (TempStatus) && (TempStatus != EFI_NOT_FOUND)) { + Status = EFI_ACCESS_DENIED; + } + + TempStatus = DeleteDbx (); + DEBUG ((DEBUG_INFO, "%a - dbx Delete = %r\n", __func__, TempStatus)); + if (EFI_ERROR (TempStatus) && (TempStatus != EFI_NOT_FOUND)) { + Status = EFI_ACCESS_DENIED; + } + + TempStatus = DeleteDbt (); + DEBUG ((DEBUG_INFO, "%a - dbt Delete = %r\n", __func__, TempStatus)); + if (EFI_ERROR (TempStatus) && (TempStatus != EFI_NOT_FOUND)) { + Status = EFI_ACCESS_DENIED; + } + + return Status; +} + +STATIC EFI_STATUS +EnrollDefaultSecureBootVariables ( + VOID + ) +{ + EFI_STATUS Status; + + Status = EnrollDbFromDefault (); + if (EFI_ERROR (Status)) { + return Status; + } + + Status = EnrollDbxFromDefault (); + if (EFI_ERROR (Status)) { + return Status; + } + + // Default dbt may not exists and is not critical error if fails + EnrollDbtFromDefault (); + + Status = EnrollKEKFromDefault (); + if (EFI_ERROR (Status)) { + return Status; + } + + return EnrollPKFromDefault (); +} + +EFI_STATUS +RestoreSecureBootDefaults ( + VOID + ) +{ + EFI_STATUS Status; + + Status = SetSecureBootMode (CUSTOM_SECURE_BOOT_MODE); + if (EFI_ERROR (Status)) { + return Status; + } + + Status = DeleteAllSecureBootVariables (); + if (EFI_ERROR (Status)) { + return Status; + } + + Status = EnrollDefaultSecureBootVariables (); + if (EFI_ERROR (Status)) { + return Status; + } + + return SetSecureBootMode (STANDARD_SECURE_BOOT_MODE); +} + +EFI_STATUS +PrepareSbVariablesForSvBoot ( + VOID + ) +{ + EFI_STATUS Status; + + Status = SetSecureBootMode (CUSTOM_SECURE_BOOT_MODE); + if (EFI_ERROR (Status)) { + return Status; + } + + Status = DeleteAllSecureBootVariables (); + if (EFI_ERROR (Status)) { + return Status; + } + + // For Sovereign Boot we want to keep dbx to prohibit booting + // known revoked, buggy and malicious images. + Status = EnrollDbxFromDefault (); + if (EFI_ERROR (Status)) { + return Status; + } + + return SetSecureBootMode (STANDARD_SECURE_BOOT_MODE); +} + +/** + Helper function to populate an EFI_TIME instance. + + @param[in] Time FileContext cached in SecureBootConfig driver + +**/ +STATIC +EFI_STATUS +GetCurrentTime ( + IN EFI_TIME *Time + ) +{ + EFI_STATUS Status; + VOID *TestPointer; + + if (Time == NULL) { + return EFI_INVALID_PARAMETER; + } + + Status = gBS->LocateProtocol (&gEfiRealTimeClockArchProtocolGuid, NULL, &TestPointer); + if (EFI_ERROR (Status)) { + return Status; + } + + ZeroMem (Time, sizeof (EFI_TIME)); + Status = gRT->GetTime (Time, NULL); + if (EFI_ERROR (Status)) { + DEBUG (( + DEBUG_ERROR, + "%a(), GetTime() failed, status = '%r'\n", + __func__, + Status + )); + return Status; + } + + Time->Pad1 = 0; + Time->Nanosecond = 0; + Time->TimeZone = 0; + Time->Daylight = 0; + Time->Pad2 = 0; + + return EFI_SUCCESS; +} + +/** + Enroll a new hash into Signature Database (DB or DBX). + + @param[in] PrivateData The module's private data. + @param[in] VariableName Variable name of signature database, must be + EFI_IMAGE_SECURITY_DATABASE or EFI_IMAGE_SECURITY_DATABASE1. + + @retval EFI_SUCCESS New X509 is enrolled successfully. + @retval EFI_OUT_OF_RESOURCES Could not allocate needed resources. + +**/ +STATIC EFI_STATUS +EnrollHashToSigDB ( + IN SOVEREIGN_BOOT_WIZARD_PRIVATE_DATA *PrivateData, + IN CHAR16 *VariableName + ) +{ + EFI_SIGNATURE_LIST *SigDBHash; + EFI_SIGNATURE_DATA *SigDBHashData; + EFI_CERT_X509_SHA256 *SigDBCertHashData; + UINTN DataSize; + UINTN SigDBSize; + UINT32 Attr; + EFI_TIME Time; + SV_MENU_ENTRY *BootloaderEntry; + SV_SECURITY_CONTEXT *SecurityContext; + SV_CERT_ENTRY *CertificateEntry; + EFI_STATUS Status; + UINT8 CertValidFrom[64]; + UINTN CertValidFromLen; + UINT8 CertValidTo[64]; + UINTN CertValidToLen; + MBED_TLS_DATETIME_OBECT *CertValidToTime; + MBED_TLS_DATETIME_OBECT CurrentTime; + EFI_INPUT_KEY Key; + + BootloaderEntry = GetMenuEntry (&BootOptionMenu, mBootloaderIndex); + if (BootloaderEntry == NULL) { + return EFI_NO_MEDIA; + } + + SecurityContext = (SV_SECURITY_CONTEXT *)BootloaderEntry->SecurityContext; + if (SecurityContext == NULL) { + return EFI_NO_MEDIA; + } + + if (SecurityContext->ImageIsSigned) { + CertificateEntry = GetCertEntry (BootloaderEntry, mCertIndex); + if (CertificateEntry == NULL) { + return EFI_NO_MEDIA; + } + } + + DataSize = 0; + SigDBSize = 0; + SigDBHash = NULL; + SigDBHashData = NULL; + SigDBSize = sizeof (EFI_SIGNATURE_LIST) + sizeof (EFI_SIGNATURE_DATA) - 1; + + if (SecurityContext->ImageIsSigned) { + SigDBSize += sizeof (EFI_CERT_X509_SHA256); + } else { + SigDBSize += SHA256_DIGEST_SIZE; + } + + SigDBHash = (EFI_SIGNATURE_LIST *) AllocateZeroPool (SigDBSize); + if (SigDBHash == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto ON_EXIT; + } + + // + // Fill Certificate Database parameters. + // + SigDBHash->SignatureListSize = (UINT32)SigDBSize; + SigDBHash->SignatureHeaderSize = 0; + SigDBHash->SignatureSize = (UINT32)(SigDBSize - sizeof (EFI_SIGNATURE_LIST)); + + SigDBHashData = (EFI_SIGNATURE_DATA *)((UINT8 *)SigDBHash + sizeof (EFI_SIGNATURE_LIST)); + CopyGuid (&SigDBHashData->SignatureOwner, &gSovereignBootWizardFormSetGuid); + if (SecurityContext->ImageIsSigned) { + CopyGuid (&SigDBHash->SignatureType, &CertificateEntry->CertType); + SigDBCertHashData = (EFI_CERT_X509_SHA256 *)SigDBHashData->SignatureData; + CopyMem ((UINT8 *)(SigDBCertHashData->ToBeSignedHash), CertificateEntry->CertDigest, CertificateEntry->CertDigestSize); + + // If enrolling certificate hash to DBX, set to revoke always (keep revocation time 0). + // If enrolling to DB, set the revocation time to the expiry date + if (StrCmp(VariableName, EFI_IMAGE_SECURITY_DATABASE) == 0) { + CertValidFromLen = 64; + CertValidToLen = 64; + + if (!X509GetValidity(CertificateEntry->CertData, + CertificateEntry->CertDataSize, + CertValidFrom, + &CertValidFromLen, + CertValidTo, + &CertValidToLen)) { + DEBUG ((DEBUG_ERROR, "Could not get certificate validity\n")); + Status = EFI_NOT_FOUND; + goto ON_EXIT; + } + + if (CertValidToLen == 0 || CertValidToLen != sizeof (MBED_TLS_DATETIME_OBECT)) { + DEBUG ((DEBUG_ERROR, "Invalid certificate validity length\n")); + Status = EFI_BAD_BUFFER_SIZE; + goto ON_EXIT; + } + + CertValidToTime = (MBED_TLS_DATETIME_OBECT *)CertValidTo; + SigDBCertHashData->TimeOfRevocation.Year = (UINT16)(CertValidToTime->Year & 0xFFFF); + SigDBCertHashData->TimeOfRevocation.Month = (UINT8)(CertValidToTime->Month & 0xFF); + SigDBCertHashData->TimeOfRevocation.Day = (UINT8)(CertValidToTime->Day & 0xFF); + SigDBCertHashData->TimeOfRevocation.Hour = (UINT8)(CertValidToTime->Hour & 0xFF); + SigDBCertHashData->TimeOfRevocation.Minute = (UINT8)(CertValidToTime->Minute & 0xFF); + SigDBCertHashData->TimeOfRevocation.Second = (UINT8)(CertValidToTime->Second & 0xFF); + } + } else { + CopyGuid (&SigDBHash->SignatureType, &SecurityContext->HashType); + CopyMem ((UINT8 *)(SigDBHashData->SignatureData), SecurityContext->ImageDigest, SecurityContext->ImageDigestSize); + } + + // + // Check if signature database entry has been already populated. + // If true, use EFI_VARIABLE_APPEND_WRITE attribute to append the + // new signature data to original variable + // + Attr = EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_RUNTIME_ACCESS + | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS; + Status = GetCurrentTime (&Time); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "Fail to fetch valid time data: %r\n", Status)); + goto ON_EXIT; + } + + // Do not allow to add expired certificate to DB + if (SecurityContext->ImageIsSigned && + (StrCmp(VariableName, EFI_IMAGE_SECURITY_DATABASE) == 0)) { + CurrentTime.Year = (INT32)Time.Year; + CurrentTime.Month = (INT32)Time.Month; + CurrentTime.Day = (INT32)Time.Day; + CurrentTime.Hour = (INT32)Time.Hour; + CurrentTime.Minute = (INT32)Time.Minute; + CurrentTime.Second = (INT32)Time.Second; + + + if (X509CompareDateTime (&CurrentTime, CertValidToTime) >= 0) { + do { + CreatePopUp ( + EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE, + &Key, + L"", + L"The certificate you want to trust has expired.", + L"Can not add it to trusted signature database.", + L"", + L"Press ENTER to abort the process...", + L"", + NULL + ); + } while (Key.UnicodeChar != CHAR_CARRIAGE_RETURN); + + Status = EFI_SUCCESS; + goto ON_EXIT; + } + } + + Status = CreateTimeBasedPayload (&SigDBSize, (UINT8 **)&SigDBHash, &Time); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "Failed to create time-based data payload: %r\n", Status)); + goto ON_EXIT; + } + + Status = gRT->GetVariable ( + VariableName, + &gEfiImageSecurityDatabaseGuid, + NULL, + &DataSize, + NULL + ); + if (Status == EFI_BUFFER_TOO_SMALL) { + Attr |= EFI_VARIABLE_APPEND_WRITE; + } else if (Status != EFI_NOT_FOUND) { + goto ON_EXIT; + } + + Status = SetSecureBootMode (CUSTOM_SECURE_BOOT_MODE); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "Failed to set custom Secure Boot mode: %r\n", Status)); + goto ON_EXIT; + } + + Status = gRT->SetVariable ( + VariableName, + &gEfiImageSecurityDatabaseGuid, + Attr, + SigDBSize, + SigDBHash + ); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "Failed to write the %s variable: %r\n", VariableName, Status)); + } else { + // If image is unsigned we have to increment the bootloader index to show + // next one. Otherwise move to next certificate. + if (!SecurityContext->ImageIsSigned) { + mBootloaderIndex++; + mCertIndex = 0; + DEBUG ((DEBUG_INFO, "Moving to next bootloader %u\n", mBootloaderIndex)); + } else { + mCertIndex++; + DEBUG ((DEBUG_INFO, "Moving to next certificate %u for bootloader %u\n", mCertIndex, mBootloaderIndex)); + } + } + + Status = SetSecureBootMode (STANDARD_SECURE_BOOT_MODE); + +ON_EXIT: + + if (SigDBHash != NULL) { + FreePool (SigDBHash); + } + + return Status; +} + +EFI_STATUS +AddKeyOrHashAsTrustedOrUntrusted ( + SOVEREIGN_BOOT_WIZARD_PRIVATE_DATA *PrivateData, + BOOLEAN Trust + ) +{ + EFI_STRING Message; + EFI_HII_POPUP_SELECTION UserSelection; + EFI_STRING HeaderString; + EFI_STRING HashString; + EFI_STRING PopupMessage; + UINTN PopupMessageSize; + EFI_STATUS Status; + CHAR16 ErrorMessage[MAXIMUM_VALUE_CHARACTERS + 8]; + EFI_INPUT_KEY Key; + + Message = HiiGetString ( + PrivateData->HiiHandle, + Trust ? STRING_TOKEN(STR_SV_TRUST_KEY_QUESTION) : STRING_TOKEN(STR_SV_UNTRUST_KEY_QUESTION), + NULL); + HeaderString = HiiGetString (PrivateData->HiiHandle, STRING_TOKEN(STR_KEY_FINGERPRINT), NULL); + HashString = HiiGetString (PrivateData->HiiHandle, STRING_TOKEN(STR_KEY_FINGERPRINT_HASH), NULL); + + // Size of the whole message plus couple new lines + PopupMessageSize = StrSize(Message) + StrSize(HeaderString) + StrSize(HashString) + 6; + PopupMessage = (CHAR16 *) AllocateZeroPool(PopupMessageSize); + + if (PopupMessage == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + UnicodeSPrint (PopupMessage, PopupMessageSize, L"%s\n%s\n%s", Message, HeaderString, HashString); + HiiSetString (PrivateData->HiiHandle, STRING_TOKEN (STR_SV_TRUST_KEY_POPUP), PopupMessage, NULL) ; + + Status = PrivateData->HiiPopup->CreatePopup ( + PrivateData->HiiPopup, + EfiHiiPopupStyleInfo, + EfiHiiPopupTypeYesNo, + PrivateData->HiiHandle, + STRING_TOKEN (STR_SV_TRUST_KEY_POPUP), + &UserSelection + ); + if (UserSelection == EfiHiiPopupSelectionYes) { + // Add key or hash to DB or DBX + if (Trust) { + Status = EnrollHashToSigDB (PrivateData, EFI_IMAGE_SECURITY_DATABASE); + } else { + Status = EnrollHashToSigDB (PrivateData, EFI_IMAGE_SECURITY_DATABASE1); + } + + if (EFI_ERROR (Status)) { + UnicodeSPrint ( + ErrorMessage, + (MAXIMUM_VALUE_CHARACTERS + 8) * sizeof (CHAR16), + L"Error: %r", + Status); + + CreatePopUp ( + EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE, + &Key, + L"", + Trust ? L"Could not add the certificate/image as trusted" : + L"Could not add the certificate/image as untrusted", + L"", + ErrorMessage, + L"", + L"Press any key to close this window...", + L"", + NULL + ); + } + } + + FreePool (PopupMessage); + + return Status; +} diff --git a/DasharoModulePkg/Application/SovereignBootWizard/SovereignBootWizard.c b/DasharoModulePkg/Application/SovereignBootWizard/SovereignBootWizard.c index 89573b566b..a6ea7f077a 100644 --- a/DasharoModulePkg/Application/SovereignBootWizard/SovereignBootWizard.c +++ b/DasharoModulePkg/Application/SovereignBootWizard/SovereignBootWizard.c @@ -211,486 +211,6 @@ RouteConfig ( return EFI_SUCCESS; } -EFI_STATUS -DeleteAllSecureBootVariables ( - VOID - ) -{ - EFI_STATUS Status, TempStatus; - - Status = DeletePlatformKey (); - DEBUG ((DEBUG_INFO, "%a - PK Delete = %r\n", __func__, Status)); - // If the PK is not found, then our work here is done. - if (Status == EFI_NOT_FOUND) { - Status = EFI_SUCCESS; - } - // If any other error occurred, let's inform the caller that the PK delete in particular failed. - else if (EFI_ERROR (Status)) { - Status = EFI_ABORTED; - } - - // - // If any of THESE steps have an error, report the error but attempt to delete all keys. - // Using TempStatus will prevent an error from being trampled by an EFI_SUCCESS. - // Overwrite Status ONLY if TempStatus is an error. - // - // If the error is EFI_NOT_FOUND, we can safely ignore it since we were trying to delete - // the variables anyway. - // - TempStatus = DeleteKEK (); - DEBUG ((DEBUG_INFO, "%a - KEK Delete = %r\n", __func__, TempStatus)); - if (EFI_ERROR (TempStatus) && (TempStatus != EFI_NOT_FOUND)) { - Status = EFI_ACCESS_DENIED; - } - - TempStatus = DeleteDb (); - DEBUG ((DEBUG_INFO, "%a - db Delete = %r\n", __func__, TempStatus)); - if (EFI_ERROR (TempStatus) && (TempStatus != EFI_NOT_FOUND)) { - Status = EFI_ACCESS_DENIED; - } - - TempStatus = DeleteDbx (); - DEBUG ((DEBUG_INFO, "%a - dbx Delete = %r\n", __func__, TempStatus)); - if (EFI_ERROR (TempStatus) && (TempStatus != EFI_NOT_FOUND)) { - Status = EFI_ACCESS_DENIED; - } - - TempStatus = DeleteDbt (); - DEBUG ((DEBUG_INFO, "%a - dbt Delete = %r\n", __func__, TempStatus)); - if (EFI_ERROR (TempStatus) && (TempStatus != EFI_NOT_FOUND)) { - Status = EFI_ACCESS_DENIED; - } - - return Status; -} - -EFI_STATUS -EnrollDefaultSecureBootVariables ( - VOID - ) -{ - EFI_STATUS Status; - - Status = EnrollDbFromDefault (); - if (EFI_ERROR (Status)) { - return Status; - } - - Status = EnrollDbxFromDefault (); - if (EFI_ERROR (Status)) { - return Status; - } - - // Default dbt may not exists and is not critical error if fails - EnrollDbtFromDefault (); - - Status = EnrollKEKFromDefault (); - if (EFI_ERROR (Status)) { - return Status; - } - - return EnrollPKFromDefault (); -} - -EFI_STATUS -RestoreSecureBootDefaults ( - VOID - ) -{ - EFI_STATUS Status; - - Status = SetSecureBootMode (CUSTOM_SECURE_BOOT_MODE); - if (EFI_ERROR (Status)) { - return Status; - } - - Status = DeleteAllSecureBootVariables (); - if (EFI_ERROR (Status)) { - return Status; - } - - Status = EnrollDefaultSecureBootVariables (); - if (EFI_ERROR (Status)) { - return Status; - } - - return SetSecureBootMode (STANDARD_SECURE_BOOT_MODE); -} - -EFI_STATUS -PrepareSbVariablesForSvBoot ( - VOID - ) -{ - EFI_STATUS Status; - - Status = SetSecureBootMode (CUSTOM_SECURE_BOOT_MODE); - if (EFI_ERROR (Status)) { - return Status; - } - - Status = DeleteAllSecureBootVariables (); - if (EFI_ERROR (Status)) { - return Status; - } - - // For Sovereign Boot we want to keep dbx to prohibit booting - // known revoked, buggy and malicious images. - Status = EnrollDbxFromDefault (); - if (EFI_ERROR (Status)) { - return Status; - } - - return SetSecureBootMode (STANDARD_SECURE_BOOT_MODE); -} - -/** - Helper function to populate an EFI_TIME instance. - - @param[in] Time FileContext cached in SecureBootConfig driver - -**/ -STATIC -EFI_STATUS -GetCurrentTime ( - IN EFI_TIME *Time - ) -{ - EFI_STATUS Status; - VOID *TestPointer; - - if (Time == NULL) { - return EFI_INVALID_PARAMETER; - } - - Status = gBS->LocateProtocol (&gEfiRealTimeClockArchProtocolGuid, NULL, &TestPointer); - if (EFI_ERROR (Status)) { - return Status; - } - - ZeroMem (Time, sizeof (EFI_TIME)); - Status = gRT->GetTime (Time, NULL); - if (EFI_ERROR (Status)) { - DEBUG (( - DEBUG_ERROR, - "%a(), GetTime() failed, status = '%r'\n", - __func__, - Status - )); - return Status; - } - - Time->Pad1 = 0; - Time->Nanosecond = 0; - Time->TimeZone = 0; - Time->Daylight = 0; - Time->Pad2 = 0; - - return EFI_SUCCESS; -} - -/** - Enroll a new hash into Signature Database (DB or DBX). - - @param[in] PrivateData The module's private data. - @param[in] VariableName Variable name of signature database, must be - EFI_IMAGE_SECURITY_DATABASE or EFI_IMAGE_SECURITY_DATABASE1. - - @retval EFI_SUCCESS New X509 is enrolled successfully. - @retval EFI_OUT_OF_RESOURCES Could not allocate needed resources. - -**/ -EFI_STATUS -EnrollHashToSigDB ( - IN SOVEREIGN_BOOT_WIZARD_PRIVATE_DATA *PrivateData, - IN CHAR16 *VariableName - ) -{ - EFI_SIGNATURE_LIST *SigDBHash; - EFI_SIGNATURE_DATA *SigDBHashData; - EFI_CERT_X509_SHA256 *SigDBCertHashData; - UINTN DataSize; - UINTN SigDBSize; - UINT32 Attr; - EFI_TIME Time; - SV_MENU_ENTRY *BootloaderEntry; - SV_SECURITY_CONTEXT *SecurityContext; - SV_CERT_ENTRY *CertificateEntry; - EFI_STATUS Status; - UINT8 CertValidFrom[64]; - UINTN CertValidFromLen; - UINT8 CertValidTo[64]; - UINTN CertValidToLen; - MBED_TLS_DATETIME_OBECT *CertValidToTime; - MBED_TLS_DATETIME_OBECT CurrentTime; - EFI_INPUT_KEY Key; - - BootloaderEntry = GetMenuEntry (&BootOptionMenu, mBootloaderIndex); - if (BootloaderEntry == NULL) { - return EFI_NO_MEDIA; - } - - SecurityContext = (SV_SECURITY_CONTEXT *)BootloaderEntry->SecurityContext; - if (SecurityContext == NULL) { - return EFI_NO_MEDIA; - } - - if (SecurityContext->ImageIsSigned) { - CertificateEntry = GetCertEntry (BootloaderEntry, mCertIndex); - if (CertificateEntry == NULL) { - return EFI_NO_MEDIA; - } - } - - DataSize = 0; - SigDBSize = 0; - SigDBHash = NULL; - SigDBHashData = NULL; - SigDBSize = sizeof (EFI_SIGNATURE_LIST) + sizeof (EFI_SIGNATURE_DATA) - 1; - - if (SecurityContext->ImageIsSigned) { - SigDBSize += sizeof (EFI_CERT_X509_SHA256); - } else { - SigDBSize += SHA256_DIGEST_SIZE; - } - - SigDBHash = (EFI_SIGNATURE_LIST *) AllocateZeroPool (SigDBSize); - if (SigDBHash == NULL) { - Status = EFI_OUT_OF_RESOURCES; - goto ON_EXIT; - } - - // - // Fill Certificate Database parameters. - // - SigDBHash->SignatureListSize = (UINT32)SigDBSize; - SigDBHash->SignatureHeaderSize = 0; - SigDBHash->SignatureSize = (UINT32)(SigDBSize - sizeof (EFI_SIGNATURE_LIST)); - - SigDBHashData = (EFI_SIGNATURE_DATA *)((UINT8 *)SigDBHash + sizeof (EFI_SIGNATURE_LIST)); - CopyGuid (&SigDBHashData->SignatureOwner, &gSovereignBootWizardFormSetGuid); - if (SecurityContext->ImageIsSigned) { - CopyGuid (&SigDBHash->SignatureType, &CertificateEntry->CertType); - SigDBCertHashData = (EFI_CERT_X509_SHA256 *)SigDBHashData->SignatureData; - CopyMem ((UINT8 *)(SigDBCertHashData->ToBeSignedHash), CertificateEntry->CertDigest, CertificateEntry->CertDigestSize); - - // If enrolling certificate hash to DBX, set to revoke always (keep revocation time 0). - // If enrolling to DB, set the revocation time to the expiry date - if (StrCmp(VariableName, EFI_IMAGE_SECURITY_DATABASE) == 0) { - CertValidFromLen = 64; - CertValidToLen = 64; - - if (!X509GetValidity(CertificateEntry->CertData, - CertificateEntry->CertDataSize, - CertValidFrom, - &CertValidFromLen, - CertValidTo, - &CertValidToLen)) { - DEBUG ((DEBUG_ERROR, "Could not get certificate validity\n")); - Status = EFI_NOT_FOUND; - goto ON_EXIT; - } - - if (CertValidToLen == 0 || CertValidToLen != sizeof (MBED_TLS_DATETIME_OBECT)) { - DEBUG ((DEBUG_ERROR, "Invalid certificate validity length\n")); - Status = EFI_BAD_BUFFER_SIZE; - goto ON_EXIT; - } - - CertValidToTime = (MBED_TLS_DATETIME_OBECT *)CertValidTo; - SigDBCertHashData->TimeOfRevocation.Year = (UINT16)(CertValidToTime->Year & 0xFFFF); - SigDBCertHashData->TimeOfRevocation.Month = (UINT8)(CertValidToTime->Month & 0xFF); - SigDBCertHashData->TimeOfRevocation.Day = (UINT8)(CertValidToTime->Day & 0xFF); - SigDBCertHashData->TimeOfRevocation.Hour = (UINT8)(CertValidToTime->Hour & 0xFF); - SigDBCertHashData->TimeOfRevocation.Minute = (UINT8)(CertValidToTime->Minute & 0xFF); - SigDBCertHashData->TimeOfRevocation.Second = (UINT8)(CertValidToTime->Second & 0xFF); - } - } else { - CopyGuid (&SigDBHash->SignatureType, &SecurityContext->HashType); - CopyMem ((UINT8 *)(SigDBHashData->SignatureData), SecurityContext->ImageDigest, SecurityContext->ImageDigestSize); - } - - // - // Check if signature database entry has been already populated. - // If true, use EFI_VARIABLE_APPEND_WRITE attribute to append the - // new signature data to original variable - // - Attr = EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_RUNTIME_ACCESS - | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS; - Status = GetCurrentTime (&Time); - if (EFI_ERROR (Status)) { - DEBUG ((DEBUG_ERROR, "Fail to fetch valid time data: %r\n", Status)); - goto ON_EXIT; - } - - // Do not allow to add expired certificate to DB - if (SecurityContext->ImageIsSigned && - (StrCmp(VariableName, EFI_IMAGE_SECURITY_DATABASE) == 0)) { - CurrentTime.Year = (INT32)Time.Year; - CurrentTime.Month = (INT32)Time.Month; - CurrentTime.Day = (INT32)Time.Day; - CurrentTime.Hour = (INT32)Time.Hour; - CurrentTime.Minute = (INT32)Time.Minute; - CurrentTime.Second = (INT32)Time.Second; - - - if (X509CompareDateTime (&CurrentTime, CertValidToTime) >= 0) { - do { - CreatePopUp ( - EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE, - &Key, - L"", - L"The certificate you want to trust has expired.", - L"Can not add it to trusted signature database.", - L"", - L"Press ENTER to abort the process...", - L"", - NULL - ); - } while (Key.UnicodeChar != CHAR_CARRIAGE_RETURN); - - Status = EFI_SUCCESS; - goto ON_EXIT; - } - } - - Status = CreateTimeBasedPayload (&SigDBSize, (UINT8 **)&SigDBHash, &Time); - if (EFI_ERROR (Status)) { - DEBUG ((DEBUG_ERROR, "Failed to create time-based data payload: %r\n", Status)); - goto ON_EXIT; - } - - Status = gRT->GetVariable ( - VariableName, - &gEfiImageSecurityDatabaseGuid, - NULL, - &DataSize, - NULL - ); - if (Status == EFI_BUFFER_TOO_SMALL) { - Attr |= EFI_VARIABLE_APPEND_WRITE; - } else if (Status != EFI_NOT_FOUND) { - DEBUG ((DEBUG_ERROR, "Failed to get %s variable size: %r\n", VariableName, Status)); - goto ON_EXIT; - } - - Status = SetSecureBootMode (CUSTOM_SECURE_BOOT_MODE); - if (EFI_ERROR (Status)) { - DEBUG ((DEBUG_ERROR, "Failed to set custom Secure Boot mode: %r\n", Status)); - goto ON_EXIT; - } - - Status = gRT->SetVariable ( - VariableName, - &gEfiImageSecurityDatabaseGuid, - Attr, - SigDBSize, - SigDBHash - ); - if (EFI_ERROR (Status)) { - DEBUG ((DEBUG_ERROR, "Failed to write the %s variable: %r\n", VariableName, Status)); - } else { - // If image is unsigned we have to increment the bootloader index to show - // next one. Otherwise move to next certificate. - if (!SecurityContext->ImageIsSigned) { - mBootloaderIndex++; - mCertIndex = 0; - DEBUG ((DEBUG_INFO, "Moving to next bootloader %u\n", mBootloaderIndex)); - } else { - mCertIndex++; - DEBUG ((DEBUG_INFO, "Moving to next certificate %u for bootloader %u\n", mCertIndex, mBootloaderIndex)); - } - } - - Status = SetSecureBootMode (STANDARD_SECURE_BOOT_MODE); - -ON_EXIT: - - if (SigDBHash != NULL) { - FreePool (SigDBHash); - } - - return Status; -} - -EFI_STATUS -AddKeyOrHashAsTrustedOrUntrusted ( - SOVEREIGN_BOOT_WIZARD_PRIVATE_DATA *PrivateData, - BOOLEAN Trust - ) -{ - EFI_STRING Message; - EFI_HII_POPUP_SELECTION UserSelection; - EFI_STRING HeaderString; - EFI_STRING HashString; - EFI_STRING PopupMessage; - UINTN PopupMessageSize; - EFI_STATUS Status; - CHAR16 ErrorMessage[MAXIMUM_VALUE_CHARACTERS + 8]; - EFI_INPUT_KEY Key; - - Message = HiiGetString ( - PrivateData->HiiHandle, - Trust ? STRING_TOKEN(STR_SV_TRUST_KEY_QUESTION) : STRING_TOKEN(STR_SV_UNTRUST_KEY_QUESTION), - NULL); - HeaderString = HiiGetString (PrivateData->HiiHandle, STRING_TOKEN(STR_KEY_FINGERPRINT), NULL); - HashString = HiiGetString (PrivateData->HiiHandle, STRING_TOKEN(STR_KEY_FINGERPRINT_HASH), NULL); - - // Size of the whole message plus couple new lines - PopupMessageSize = StrSize(Message) + StrSize(HeaderString) + StrSize(HashString) + 6; - PopupMessage = (CHAR16 *) AllocateZeroPool(PopupMessageSize); - - if (PopupMessage == NULL) { - return EFI_OUT_OF_RESOURCES; - } - - UnicodeSPrint (PopupMessage, PopupMessageSize, L"%s\n%s\n%s", Message, HeaderString, HashString); - HiiSetString (PrivateData->HiiHandle, STRING_TOKEN (STR_SV_TRUST_KEY_POPUP), PopupMessage, NULL) ; - - Status = PrivateData->HiiPopup->CreatePopup ( - PrivateData->HiiPopup, - EfiHiiPopupStyleInfo, - EfiHiiPopupTypeYesNo, - PrivateData->HiiHandle, - STRING_TOKEN (STR_SV_TRUST_KEY_POPUP), - &UserSelection - ); - if (UserSelection == EfiHiiPopupSelectionYes) { - // Add key or hash to DB or DBX - if (Trust) { - Status = EnrollHashToSigDB (PrivateData, EFI_IMAGE_SECURITY_DATABASE); - } else { - Status = EnrollHashToSigDB (PrivateData, EFI_IMAGE_SECURITY_DATABASE1); - } - - if (EFI_ERROR (Status)) { - UnicodeSPrint ( - ErrorMessage, - (MAXIMUM_VALUE_CHARACTERS + 8) * sizeof (CHAR16), - L"Error: %r", - Status); - - CreatePopUp ( - EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE, - &Key, - L"", - Trust ? L"Could not add the certificate/image as trusted" : - L"Could not add the certificate/image as untrusted", - L"", - ErrorMessage, - L"", - L"Press any key to close this window...", - L"", - NULL - ); - } - } - - FreePool (PopupMessage); - - return Status; -} - EFI_STATUS BootTheBootloader ( SOVEREIGN_BOOT_WIZARD_PRIVATE_DATA *PrivateData diff --git a/DasharoModulePkg/Application/SovereignBootWizard/SovereignBootWizard.h b/DasharoModulePkg/Application/SovereignBootWizard/SovereignBootWizard.h index 2799fcc5ad..3fc2791e46 100644 --- a/DasharoModulePkg/Application/SovereignBootWizard/SovereignBootWizard.h +++ b/DasharoModulePkg/Application/SovereignBootWizard/SovereignBootWizard.h @@ -243,4 +243,20 @@ UpdateCertDetails ( IN SOVEREIGN_BOOT_WIZARD_PRIVATE_DATA *Private ); +EFI_STATUS +RestoreSecureBootDefaults ( + VOID + ); + +EFI_STATUS +PrepareSbVariablesForSvBoot ( + VOID + ); + +EFI_STATUS +AddKeyOrHashAsTrustedOrUntrusted ( + SOVEREIGN_BOOT_WIZARD_PRIVATE_DATA *PrivateData, + BOOLEAN Trust + ); + #endif diff --git a/DasharoModulePkg/Application/SovereignBootWizard/SovereignBootWizard.inf b/DasharoModulePkg/Application/SovereignBootWizard/SovereignBootWizard.inf index 52ae9d3b6c..c397ec7621 100644 --- a/DasharoModulePkg/Application/SovereignBootWizard/SovereignBootWizard.inf +++ b/DasharoModulePkg/Application/SovereignBootWizard/SovereignBootWizard.inf @@ -27,6 +27,7 @@ [Sources] BootOptionParsing.c + KeyManagement.c SignatureParsing.c SovereignBootWizard.c SovereignBootWizardHii.h From f1328b475084b4b693856c4f5c26374b28b1d114 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20=C5=BBygowski?= Date: Fri, 25 Jul 2025 18:29:15 +0200 Subject: [PATCH 13/28] DMP/Application/SovereignBootWizard: Add ephemeral PK and finalize provisioning MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Michał Żygowski --- .../SovereignBootWizard/BootOptionParsing.c | 32 +- .../SovereignBootWizard/KeyManagement.c | 385 +++++++++++++++--- .../SovereignBootWizard/SignatureParsing.c | 58 ++- .../SovereignBootWizard/SovereignBootWizard.c | 147 +++++-- .../SovereignBootWizard/SovereignBootWizard.h | 19 + .../SovereignBootWizard.inf | 6 +- 6 files changed, 522 insertions(+), 125 deletions(-) diff --git a/DasharoModulePkg/Application/SovereignBootWizard/BootOptionParsing.c b/DasharoModulePkg/Application/SovereignBootWizard/BootOptionParsing.c index cc7ea93da3..567a8f02a0 100644 --- a/DasharoModulePkg/Application/SovereignBootWizard/BootOptionParsing.c +++ b/DasharoModulePkg/Application/SovereignBootWizard/BootOptionParsing.c @@ -47,38 +47,28 @@ UiDevicePathToStr ( } /** - Check if it's a Device Path pointing to FV file. - - The function doesn't guarantee the device path points to existing FV file. + Check if it's a Device Path pointing to HDD. @param DevicePath Input device path. - @retval TRUE The device path is a FV File Device Path. - @retval FALSE The device path is NOT a FV File Device Path. + @retval TRUE The device path is a HDD Device Path. + @retval FALSE The device path is NOT a HDD File Device Path. **/ BOOLEAN -IsFvFilePath ( +IsHddFilePath ( IN EFI_DEVICE_PATH_PROTOCOL *DevicePath ) { - EFI_STATUS Status; - EFI_HANDLE Handle; - EFI_DEVICE_PATH_PROTOCOL *Node; EFI_DEVICE_PATH_PROTOCOL *Path; - Node = DevicePath; - Status = gBS->LocateDevicePath (&gEfiFirmwareVolume2ProtocolGuid, &Node, &Handle); - if (!EFI_ERROR (Status)) { - return TRUE; - } - Path = DevicePath; - if ((DevicePathType (Path) == HARDWARE_DEVICE_PATH) && (DevicePathSubType (Path) == HW_MEMMAP_DP)) { - Path = NextDevicePathNode (Path); - if ((DevicePathType (Path) == MEDIA_DEVICE_PATH) && (DevicePathSubType (Path) == MEDIA_PIWG_FW_FILE_DP)) { - return IsDevicePathEnd (NextDevicePathNode (Path)); + while (!IsDevicePathEnd (Path)) { + if ((DevicePathType (Path) == MEDIA_DEVICE_PATH) && (DevicePathSubType (Path) == MEDIA_HARDDRIVE_DP)) { + return TRUE; } + + Path = NextDevicePathNode (Path); } return FALSE; @@ -279,8 +269,8 @@ GetBootOptions ( // DevicePath = (EFI_DEVICE_PATH_PROTOCOL *)Ptr; - // Skip boot options that point to FV - if (IsFvFilePath (DevicePath)) { + // Skip boot options that do not point to disks + if (!IsHddFilePath (DevicePath)) { FreePool (LoadOptionFromVar); continue; } diff --git a/DasharoModulePkg/Application/SovereignBootWizard/KeyManagement.c b/DasharoModulePkg/Application/SovereignBootWizard/KeyManagement.c index 08f7a4834b..a5c3de376c 100644 --- a/DasharoModulePkg/Application/SovereignBootWizard/KeyManagement.c +++ b/DasharoModulePkg/Application/SovereignBootWizard/KeyManagement.c @@ -186,6 +186,54 @@ GetCurrentTime ( return EFI_SUCCESS; } +/** Creates EFI Signature List structure. + + @param[in] Data A pointer to signature data. + @param[in] Size Size of signature data. + @param[in] SigType GUID representing signature type. + @param[out] SigList Created Signature List. + + @retval EFI_SUCCESS Signature List was created successfully. + @retval EFI_OUT_OF_RESOURCES Failed to allocate memory. +**/ +EFI_STATUS +CreateSigList ( + IN VOID *Data, + IN UINTN Size, + IN EFI_GUID *SigType, + OUT EFI_SIGNATURE_LIST **SigList + ) +{ + UINTN SigListSize; + EFI_SIGNATURE_LIST *TmpSigList; + EFI_SIGNATURE_DATA *SigData; + + // + // Allocate data for Signature Database + // + SigListSize = sizeof (EFI_SIGNATURE_LIST) + sizeof (EFI_SIGNATURE_DATA) - 1 + Size; + TmpSigList = (EFI_SIGNATURE_LIST *)AllocateZeroPool (SigListSize); + if (TmpSigList == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + TmpSigList->SignatureListSize = (UINT32)SigListSize; + TmpSigList->SignatureSize = (UINT32)(sizeof (EFI_SIGNATURE_DATA) - 1 + Size); + TmpSigList->SignatureHeaderSize = 0; + CopyGuid (&TmpSigList->SignatureType, SigType); + + // + // Copy key data + // + SigData = (EFI_SIGNATURE_DATA *)(TmpSigList + 1); + CopyGuid (&SigData->SignatureOwner, &gSovereignBootWizardFormSetGuid); + CopyMem (&SigData->SignatureData[0], Data, Size); + + *SigList = TmpSigList; + + return EFI_SUCCESS; +} + /** Enroll a new hash into Signature Database (DB or DBX). @@ -204,8 +252,7 @@ EnrollHashToSigDB ( ) { EFI_SIGNATURE_LIST *SigDBHash; - EFI_SIGNATURE_DATA *SigDBHashData; - EFI_CERT_X509_SHA256 *SigDBCertHashData; + EFI_CERT_X509_SHA256 SigCertHashData; UINTN DataSize; UINTN SigDBSize; UINT32 Attr; @@ -218,9 +265,9 @@ EnrollHashToSigDB ( UINTN CertValidFromLen; UINT8 CertValidTo[64]; UINTN CertValidToLen; - MBED_TLS_DATETIME_OBECT *CertValidToTime; MBED_TLS_DATETIME_OBECT CurrentTime; EFI_INPUT_KEY Key; + BOOLEAN Trust; BootloaderEntry = GetMenuEntry (&BootOptionMenu, mBootloaderIndex); if (BootloaderEntry == NULL) { @@ -242,69 +289,50 @@ EnrollHashToSigDB ( DataSize = 0; SigDBSize = 0; SigDBHash = NULL; - SigDBHashData = NULL; + Trust = (StrCmp(VariableName, EFI_IMAGE_SECURITY_DATABASE) == 0); + SigDBSize = sizeof (EFI_SIGNATURE_LIST) + sizeof (EFI_SIGNATURE_DATA) - 1; if (SecurityContext->ImageIsSigned) { - SigDBSize += sizeof (EFI_CERT_X509_SHA256); + // EDK2 only checks X509 certificates in DB, so enroll whole cert to DB. + // Otherwise enroll SHA256 to DBX to save space. + if (Trust) { + SigDBSize += CertificateEntry->CertDataSize; + Status = CreateSigList ( + CertificateEntry->CertData, + CertificateEntry->CertDataSize, + &gEfiCertX509Guid, + &SigDBHash + ); + } else { + SigDBSize += sizeof (EFI_CERT_X509_SHA256); + // If enrolling certificate hash to DBX, set to revoke always (keep revocation time 0). + SetMem (&SigCertHashData.TimeOfRevocation, 0, sizeof(SigCertHashData.TimeOfRevocation)); + CopyMem (&SigCertHashData.ToBeSignedHash, CertificateEntry->CertDigest, CertificateEntry->CertDigestSize); + Status = CreateSigList ( + &SigCertHashData, + sizeof (EFI_CERT_X509_SHA256), + &CertificateEntry->CertType, + &SigDBHash + ); + } } else { SigDBSize += SHA256_DIGEST_SIZE; + Status = CreateSigList ( + SecurityContext->ImageDigest, + SecurityContext->ImageDigestSize, + &SecurityContext->HashType, + &SigDBHash + ); } - SigDBHash = (EFI_SIGNATURE_LIST *) AllocateZeroPool (SigDBSize); if (SigDBHash == NULL) { Status = EFI_OUT_OF_RESOURCES; goto ON_EXIT; } - // - // Fill Certificate Database parameters. - // - SigDBHash->SignatureListSize = (UINT32)SigDBSize; - SigDBHash->SignatureHeaderSize = 0; - SigDBHash->SignatureSize = (UINT32)(SigDBSize - sizeof (EFI_SIGNATURE_LIST)); - - SigDBHashData = (EFI_SIGNATURE_DATA *)((UINT8 *)SigDBHash + sizeof (EFI_SIGNATURE_LIST)); - CopyGuid (&SigDBHashData->SignatureOwner, &gSovereignBootWizardFormSetGuid); - if (SecurityContext->ImageIsSigned) { - CopyGuid (&SigDBHash->SignatureType, &CertificateEntry->CertType); - SigDBCertHashData = (EFI_CERT_X509_SHA256 *)SigDBHashData->SignatureData; - CopyMem ((UINT8 *)(SigDBCertHashData->ToBeSignedHash), CertificateEntry->CertDigest, CertificateEntry->CertDigestSize); - - // If enrolling certificate hash to DBX, set to revoke always (keep revocation time 0). - // If enrolling to DB, set the revocation time to the expiry date - if (StrCmp(VariableName, EFI_IMAGE_SECURITY_DATABASE) == 0) { - CertValidFromLen = 64; - CertValidToLen = 64; - - if (!X509GetValidity(CertificateEntry->CertData, - CertificateEntry->CertDataSize, - CertValidFrom, - &CertValidFromLen, - CertValidTo, - &CertValidToLen)) { - DEBUG ((DEBUG_ERROR, "Could not get certificate validity\n")); - Status = EFI_NOT_FOUND; - goto ON_EXIT; - } - - if (CertValidToLen == 0 || CertValidToLen != sizeof (MBED_TLS_DATETIME_OBECT)) { - DEBUG ((DEBUG_ERROR, "Invalid certificate validity length\n")); - Status = EFI_BAD_BUFFER_SIZE; - goto ON_EXIT; - } - - CertValidToTime = (MBED_TLS_DATETIME_OBECT *)CertValidTo; - SigDBCertHashData->TimeOfRevocation.Year = (UINT16)(CertValidToTime->Year & 0xFFFF); - SigDBCertHashData->TimeOfRevocation.Month = (UINT8)(CertValidToTime->Month & 0xFF); - SigDBCertHashData->TimeOfRevocation.Day = (UINT8)(CertValidToTime->Day & 0xFF); - SigDBCertHashData->TimeOfRevocation.Hour = (UINT8)(CertValidToTime->Hour & 0xFF); - SigDBCertHashData->TimeOfRevocation.Minute = (UINT8)(CertValidToTime->Minute & 0xFF); - SigDBCertHashData->TimeOfRevocation.Second = (UINT8)(CertValidToTime->Second & 0xFF); - } - } else { - CopyGuid (&SigDBHash->SignatureType, &SecurityContext->HashType); - CopyMem ((UINT8 *)(SigDBHashData->SignatureData), SecurityContext->ImageDigest, SecurityContext->ImageDigestSize); + if (EFI_ERROR (Status)) { + goto ON_EXIT; } // @@ -321,8 +349,7 @@ EnrollHashToSigDB ( } // Do not allow to add expired certificate to DB - if (SecurityContext->ImageIsSigned && - (StrCmp(VariableName, EFI_IMAGE_SECURITY_DATABASE) == 0)) { + if (SecurityContext->ImageIsSigned && Trust) { CurrentTime.Year = (INT32)Time.Year; CurrentTime.Month = (INT32)Time.Month; CurrentTime.Day = (INT32)Time.Day; @@ -330,8 +357,27 @@ EnrollHashToSigDB ( CurrentTime.Minute = (INT32)Time.Minute; CurrentTime.Second = (INT32)Time.Second; + // If enrolling to DB, check the expiry date + CertValidFromLen = 64; + CertValidToLen = 64; + if (!X509GetValidity(CertificateEntry->CertData, + CertificateEntry->CertDataSize, + CertValidFrom, + &CertValidFromLen, + CertValidTo, + &CertValidToLen)) { + DEBUG ((DEBUG_ERROR, "Could not get certificate validity\n")); + Status = EFI_NOT_FOUND; + goto ON_EXIT; + } - if (X509CompareDateTime (&CurrentTime, CertValidToTime) >= 0) { + if (CertValidToLen == 0 || CertValidToLen != sizeof (MBED_TLS_DATETIME_OBECT)) { + DEBUG ((DEBUG_ERROR, "Invalid certificate validity length\n")); + Status = EFI_BAD_BUFFER_SIZE; + goto ON_EXIT; + } + + if (X509CompareDateTime (&CurrentTime, CertValidTo) >= 0) { do { CreatePopUp ( EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE, @@ -455,6 +501,9 @@ AddKeyOrHashAsTrustedOrUntrusted ( // Add key or hash to DB or DBX if (Trust) { Status = EnrollHashToSigDB (PrivateData, EFI_IMAGE_SECURITY_DATABASE); + if (!EFI_ERROR (Status) && (mFirstTrustedBootloader == -1)) { + mFirstTrustedBootloader = (INTN)mBootloaderIndex; + } } else { Status = EnrollHashToSigDB (PrivateData, EFI_IMAGE_SECURITY_DATABASE1); } @@ -480,9 +529,231 @@ AddKeyOrHashAsTrustedOrUntrusted ( NULL ); } + } else { + Status = EFI_ABORTED; } FreePool (PopupMessage); return Status; } + +STATIC EFI_STATUS +EnrollEphemeralPk ( + VOID + ) +{ + VOID *RsaCtx; + EFI_SIGNATURE_LIST *SigList; + EFI_SIGNATURE_DATA *SigData; + UINTN SigListSize; + UINTN KeySize; + EFI_STATUS Status; + UINT32 Attr; + EFI_TIME Time; + CONST UINT32 Exponent = 0x10001; + + KeySize = 2048; + SigList = NULL; + + RsaCtx = RsaNew (); + if (RsaCtx == NULL) { + DEBUG ((DEBUG_ERROR, "Failed to allocate RSA context\n")); + return EFI_OUT_OF_RESOURCES; + } + + DEBUG ((DEBUG_ERROR, "Generating ephemeral PK...\n")); + + // Size of the exponent must be 3, because it is treated as big endian. It + // also has a side effect that no matter the endianness of the host, the + // exponent will be correct. + if (!RsaGenerateKey (RsaCtx, KeySize, (CONST UINT8 *)&Exponent, sizeof(Exponent) - 1)) { + DEBUG ((DEBUG_ERROR, "Failed to generate RSA key\n")); + return EFI_DEVICE_ERROR; + } + + DEBUG ((DEBUG_ERROR, "PK generation done. Contrsuting signature list and enroling it\n")); + + KeySize = KeySize / 8; + SigListSize = sizeof (EFI_SIGNATURE_LIST) + sizeof (EFI_SIGNATURE_DATA) - 1 + KeySize; + SigList = (EFI_SIGNATURE_LIST *) AllocateZeroPool (SigListSize); + if (SigList == NULL) { + DEBUG ((DEBUG_ERROR, "Failed to allocate signature list for PK\n")); + return EFI_OUT_OF_RESOURCES; + } + + SigList->SignatureHeaderSize = 0; + SigList->SignatureListSize = SigListSize; + SigList->SignatureSize = (UINT32)(SigListSize - sizeof (EFI_SIGNATURE_LIST)); + CopyGuid(&SigList->SignatureType, &gEfiCertRsa2048Guid); + + SigData = (EFI_SIGNATURE_DATA *)((UINT8 *)SigList + sizeof (EFI_SIGNATURE_LIST)); + CopyGuid(&SigData->SignatureOwner, &gSovereignBootWizardFormSetGuid); + + if (!RsaGetKey(RsaCtx, RsaKeyN, SigData->SignatureData, &KeySize)) { + DEBUG ((DEBUG_ERROR, "Failed toget RSA public key modulus\n")); + Status = EFI_OUT_OF_RESOURCES; + goto ON_EXIT; + } + + if (KeySize != (2048 / 8)) { + DEBUG ((DEBUG_ERROR, "Invalid RSA key modulus size\n")); + Status = EFI_BAD_BUFFER_SIZE; + goto ON_EXIT; + } + + Status = GetCurrentTime (&Time); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "Could not get current time\n")); + goto ON_EXIT; + } + + Attr = EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_RUNTIME_ACCESS + | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS; + + Status = CreateTimeBasedPayload (&SigListSize, (UINT8 **)&SigList, &Time); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "Failed to create time-based data payload: %r\n", Status)); + goto ON_EXIT; + } + + Status = SetSecureBootMode (CUSTOM_SECURE_BOOT_MODE); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "Failed to set custom Secure Boot mode: %r\n", Status)); + goto ON_EXIT; + } + + Status = gRT->SetVariable ( + EFI_PLATFORM_KEY_NAME, + &gEfiGlobalVariableGuid, + Attr, + SigListSize, + SigList + ); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "Failed to write the PK variable: %r\n", Status)); + } + + Status = SetSecureBootMode (STANDARD_SECURE_BOOT_MODE); + +ON_EXIT: + + if (SigList != NULL) { + FreePool (SigList); + } + + if (RsaCtx != NULL) { + RsaFree (RsaCtx); + } + + return Status; +} + +BOOLEAN +IsDbEmpty ( + VOID +) +{ + EFI_STATUS Status; + UINTN DataSize; + + DataSize = 0; + + Status = gRT->GetVariable ( + EFI_IMAGE_SECURITY_DATABASE, + &gEfiImageSecurityDatabaseGuid, + NULL, + &DataSize, + NULL + ); + if (Status == EFI_BUFFER_TOO_SMALL) { + // Variable exists and size is greater than 0, so not empty + return FALSE; + } else if (Status == EFI_SUCCESS && DataSize > 0) { + return FALSE; + } + + return TRUE; +} + +EFI_STATUS +FinalizeSvBootProvisioning ( + VOID + ) +{ + EFI_INPUT_KEY Key; + EFI_STATUS Status; + UINTN DataSize; + SOVEREIGN_BOOT_WIZARD_NV_CONFIG SvConfig; + + if (IsDbEmpty ()) { + CreatePopUp ( + EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE, + &Key, + L"", + L"Trusted signature database is empty, because you have not trusted any bootloader", + L"Can not finalize Sovereign Boot provisioning.", + L"", + L"Press any key to close this window...", + L"", + NULL + ); + + return EFI_ABORTED; + } + + CreatePopUp ( + EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE, + NULL, + L"", + L"Generating ephemeral key, it may take a while...", + L"", + NULL + ); + + Status = EnrollEphemeralPk (); + if (EFI_ERROR (Status)) { + CreatePopUp ( + EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE, + &Key, + L"", + L"Failed to enroll ephemeral Platform Key, Secure Boot will not be enabled.", + L"Can not finalize Sovereign Boot provisioning.", + L"", + L"Press any key to close this window...", + L"", + NULL + ); + + return EFI_ABORTED; + } + + DataSize = sizeof (SOVEREIGN_BOOT_WIZARD_NV_CONFIG); + SvConfig.SvBootEnabled = TRUE; + SvConfig.SvBootProvisioned = TRUE; + Status = gRT->SetVariable ( + SV_BOOT_CONFIG_VAR, + &gSovereignBootWizardFormSetGuid, + EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS, + DataSize, + &SvConfig + ); + + if (EFI_ERROR (Status)) { + CreatePopUp ( + EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE, + &Key, + L"", + L"Failed to update, Sovereign Boot internal state to provisioned.", + L"The wizard will show up again on next boot to provision again.", + L"", + L"Press any key to close this window...", + L"", + NULL + ); + + return EFI_ABORTED; + } + + return Status; +} diff --git a/DasharoModulePkg/Application/SovereignBootWizard/SignatureParsing.c b/DasharoModulePkg/Application/SovereignBootWizard/SignatureParsing.c index a53fba8265..807306ce91 100644 --- a/DasharoModulePkg/Application/SovereignBootWizard/SignatureParsing.c +++ b/DasharoModulePkg/Application/SovereignBootWizard/SignatureParsing.c @@ -68,24 +68,26 @@ VerifyImageHashInDatabases ( continue; } + IsFound = FALSE; Status = IsSignatureFoundInDatabase ( - EFI_IMAGE_SECURITY_DATABASE1, - ImageDigest, - &CertType, - ImageDigestSize, - &IsFound - ); - if (!EFI_ERROR (Status) && IsFound) { + EFI_IMAGE_SECURITY_DATABASE1, + ImageDigest, + &CertType, + ImageDigestSize, + &IsFound + ); + if (EFI_ERROR (Status) || IsFound) { SecCtx->ImageIsInDbx = TRUE; } + IsFound = FALSE; Status = IsSignatureFoundInDatabase ( - EFI_IMAGE_SECURITY_DATABASE, - ImageDigest, - &CertType, - ImageDigestSize, - &IsFound - ); + EFI_IMAGE_SECURITY_DATABASE, + ImageDigest, + &CertType, + ImageDigestSize, + &IsFound + ); if (!EFI_ERROR (Status) && IsFound) { SecCtx->ImageIsInDb = TRUE; } @@ -118,6 +120,9 @@ FillCertificateEntries ( UINTN TrustedCertLength; UINTN CertCount; EFI_GUID CertType; + UINTN Index; + EFI_STATUS Status; + BOOLEAN IsFound; CertCount = 0; // @@ -242,9 +247,28 @@ FillCertificateEntries ( continue; } + // // Verify the digital signature and check against databases - NewCertEntry->CertIsInDb = IsAllowedByDb (AuthData, AuthDataSize, ImageDigest, ImageDigestSize); + // + + // EDK2 checks the DB only against full X509 certificates + IsFound = FALSE; + Status = IsSignatureFoundInDatabase ( + EFI_IMAGE_SECURITY_DATABASE, + NewCertEntry->CertData, + &gEfiCertX509Guid, + NewCertEntry->CertDataSize, + &IsFound + ); + DEBUG ((DEBUG_INFO, "IsSignatureFoundInDatabase: %u, %r\n", IsFound, Status)); + if (!EFI_ERROR (Status) && IsFound) { + NewCertEntry->CertIsInDb = TRUE; + } else { + NewCertEntry->CertIsInDb = FALSE; + } + NewCertEntry->CertIsInDbx = IsForbiddenByDbx (AuthData, AuthDataSize, ImageDigest, ImageDigestSize); + // TODO, doesn't work. Iterate over whole EFI_CERT_STACK of CertBuffer? NewCertEntry->ImageIsVerified = AuthenticodeVerify ( AuthData, AuthDataSize, TrustedCert, TrustedCertLength, @@ -261,6 +285,12 @@ FillCertificateEntries ( NewCertEntry->CertIsInDbx, NewCertEntry->CertIsMicrosoft)); + DEBUG ((DEBUG_INFO, "Certificate hash:\n")); + for (Index = 0; Index < NewCertEntry->CertDigestSize; Index++) { + DEBUG ((DEBUG_INFO, "%02X", NewCertEntry->CertDigest[Index])); + } + DEBUG ((DEBUG_INFO, "\n")); + InsertTailList (&SecCtx->Certs, &NewCertEntry->CertLink); CertCount++; } diff --git a/DasharoModulePkg/Application/SovereignBootWizard/SovereignBootWizard.c b/DasharoModulePkg/Application/SovereignBootWizard/SovereignBootWizard.c index a6ea7f077a..6f01a2dee1 100644 --- a/DasharoModulePkg/Application/SovereignBootWizard/SovereignBootWizard.c +++ b/DasharoModulePkg/Application/SovereignBootWizard/SovereignBootWizard.c @@ -41,6 +41,7 @@ STATIC HII_VENDOR_DEVICE_PATH mHiiVendorDevicePath = { UINTN mBootloaderIndex = 0; UINTN mCertIndex = 0; +INTN mFirstTrustedBootloader = -1; EFI_STATUS EFIAPI @@ -213,7 +214,8 @@ RouteConfig ( EFI_STATUS BootTheBootloader ( - SOVEREIGN_BOOT_WIZARD_PRIVATE_DATA *PrivateData + SOVEREIGN_BOOT_WIZARD_PRIVATE_DATA *PrivateData, + UINTN BootloaderIndex ) { SV_MENU_ENTRY *BootloaderEntry; @@ -221,7 +223,7 @@ BootTheBootloader ( EFI_BOOT_MANAGER_LOAD_OPTION BootOption; EFI_STATUS Status; - BootloaderEntry = GetMenuEntry (&BootOptionMenu, mBootloaderIndex); + BootloaderEntry = GetMenuEntry (&BootOptionMenu, BootloaderIndex); if (BootloaderEntry == NULL) { return EFI_NO_MEDIA; } @@ -251,13 +253,13 @@ BootTheBootloader ( gST->ConOut->ClearScreen (gST->ConOut); } + // TODO: Make this bootloader the first boot priority + DEBUG ((DEBUG_INFO, "Booting %s\n", BootloaderContext->Description)); EfiBootManagerBoot (&BootOption); EfiBootManagerFreeLoadOption (&BootOption); - // In case we get back from the booted bootloader, we have to update the - // page so that it will display next bootloader or key. - return UpdateBootloaderPage (PrivateData); + return EFI_SUCCESS; } /** @@ -371,7 +373,6 @@ Callback ( Status = AddKeyOrHashAsTrustedOrUntrusted(PrivateData, FALSE); break; } - case TRUST_KEY_AND_BOOT_FORM2_QUESTION_ID: case TRUST_KEY_FORM2_QUESTION_ID: { // Add cert or image hash to DB @@ -416,34 +417,81 @@ Callback ( EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE, &Key, L"", - L"No more bootloaders found.", + L"No more bootloaders found, to finalize provisioning process", + L"the Wizard will create ephemeral PK and enable UEFI Secure Boot.", L"", L"Press ENTER to continue...", L"", NULL ); } while (Key.UnicodeChar != CHAR_CARRIAGE_RETURN); - // TODO: Here + // 1. If nothing has been selected as trusted so far, warn popup. // 2. Create ephemeral PK. Enroll it and enable Secure Boot. // 3. Set the SV boot variable to provisioned state. - // 4. Boot the first trusted bootloader. - - // No more bootloaders, exit the formset, reset the bootloader - // index and go to ineractive mode form for now. - PrivateData->FormBrowserEx2->SetScope (FormSetLevel); - Status = PrivateData->FormBrowserEx2->ExecuteAction(BROWSER_ACTION_EXIT, 0); - mBootloaderIndex = 0; - Status = PrivateData->FormBrowser2->SendForm ( - PrivateData->FormBrowser2, - &PrivateData->HiiHandle, - 1, - &gSovereignBootWizardFormSetGuid, - SOVEREIGN_BOOT_WIZARD_INTERACTIVE_MODE_FORM_ID, - NULL, - ActionRequest - ); - } else if (Status == EFI_NOT_FOUND) { + // FinalizeSvBootProvisioning will handle all above + Status = FinalizeSvBootProvisioning (); + if (!EFI_ERROR (Status)) { + if (mFirstTrustedBootloader != -1) { + do { + CreatePopUp ( + EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE, + &Key, + L"", + L"Sovereign Boot provisioning successful.", + L"The Wizard will now boot the first trusted bootloader.", + L"", + L"Press ENTER to boot...", + L"", + NULL + ); + } while (Key.UnicodeChar != CHAR_CARRIAGE_RETURN); + // 4. Boot the first trusted bootloader. + BootTheBootloader(PrivateData, (UINTN)mFirstTrustedBootloader); + // If we return from the bootloader, exit the form completely. + mBootloaderIndex = 0; + if (PrivateData->ConfigData.AppLaunchCause == SV_BOOT_LAUNCH_VIA_SETUP) { + Scope = FormSetLevel; + } else { + Scope = SystemLevel; + } + PrivateData->FormBrowserEx2->SetScope (Scope); + Status = PrivateData->FormBrowserEx2->ExecuteAction(BROWSER_ACTION_EXIT, 0); + + *ActionRequest = EFI_BROWSER_ACTION_REQUEST_EXIT; + } else { + do { + CreatePopUp ( + EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE, + &Key, + L"", + L"Could not find first trusted bootloader.", + L"Wizard will reset the system and let firmware decide what to boot next\n", + L"Press ENTER to reset the system...", + L"", + NULL + ); + } while (Key.UnicodeChar != CHAR_CARRIAGE_RETURN); + + gRT->ResetSystem (EfiResetCold, EFI_SUCCESS, 0, NULL); + } + } else { + // Unexpected failure when finalizing the provisioning, reset + // bootloader index and go back to welcome form + mBootloaderIndex = 0; + PrivateData->FormBrowserEx2->SetScope (FormSetLevel); + PrivateData->FormBrowserEx2->ExecuteAction(BROWSER_ACTION_EXIT, 0); + Status = PrivateData->FormBrowser2->SendForm ( + PrivateData->FormBrowser2, + &PrivateData->HiiHandle, + 1, + &gSovereignBootWizardFormSetGuid, + SOVEREIGN_BOOT_WIZARD_WELCOME_FORM_ID, + NULL, + ActionRequest + ); + } + } else if (EFI_ERROR(Status)) { do { CreatePopUp ( EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE, @@ -469,15 +517,42 @@ Callback ( ActionRequest ); } - } else { - Status = EFI_NO_MEDIA; } break; case TRUST_KEY_AND_BOOT_FORM2_QUESTION_ID: + // Add cert or image hash to DB + Status = AddKeyOrHashAsTrustedOrUntrusted(PrivateData, TRUE); + if (EFI_ERROR (Status)) { + return Status; + } // All is left here is to enroll PK to enable Secure Boot, set // Sovereign Boot to provisioned and boot. - - Status = BootTheBootloader (PrivateData); + Status = FinalizeSvBootProvisioning (); + if (!EFI_ERROR (Status)) { + CreatePopUp ( + EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE, + NULL, + L"", + L"Sovereign Boot provisioning successful.", + L"The Wizard will now boot the selected bootloader.", + L"", + NULL + ); + Status = BootTheBootloader (PrivateData, mBootloaderIndex); + } else { + // Unexpected failure when finalizing the provisioning, reset + // bootloader index and go back to welcome form + mBootloaderIndex = 0; + Status = PrivateData->FormBrowser2->SendForm ( + PrivateData->FormBrowser2, + &PrivateData->HiiHandle, + 1, + &gSovereignBootWizardFormSetGuid, + SOVEREIGN_BOOT_WIZARD_WELCOME_FORM_ID, + NULL, + ActionRequest + ); + } break; default: break; @@ -505,7 +580,7 @@ Callback ( L"", L"Could not find any bootloaders.", L"", - L"Press ENTER to continue...", + L"Press ENTER to exit Sovereign Boot configuration...", L"", NULL ); @@ -677,7 +752,7 @@ SovereignBootWizardInit ( mPrivateData->HiiPopup = PopupHandler; Status = gBS->LocateProtocol (&gEfiDevicePathToTextProtocolGuid, NULL, (VOID **)&DevPathToText); - if (EFI_ERROR (Status)) { + if (EFI_ERROR (Status)) { ASSERT_EFI_ERROR (Status); return Status; } @@ -692,7 +767,15 @@ SovereignBootWizardInit ( &mPrivateData->ConfigAccess, NULL ); - ASSERT_EFI_ERROR (Status); + + if (Status == EFI_ALREADY_STARTED) { + Status = EFI_SUCCESS; + } + + if (EFI_ERROR (Status)) { + SovereignBootWizardUnload (ImageHandle); + return Status; + } mPrivateData->AppHandle = AppHandle; diff --git a/DasharoModulePkg/Application/SovereignBootWizard/SovereignBootWizard.h b/DasharoModulePkg/Application/SovereignBootWizard/SovereignBootWizard.h index 3fc2791e46..a18ff88f3c 100644 --- a/DasharoModulePkg/Application/SovereignBootWizard/SovereignBootWizard.h +++ b/DasharoModulePkg/Application/SovereignBootWizard/SovereignBootWizard.h @@ -204,6 +204,7 @@ typedef struct { extern SV_MENU_OPTION BootOptionMenu; extern UINTN mBootloaderIndex; extern UINTN mCertIndex; +extern INTN mFirstTrustedBootloader; EFI_STATUS GetBootOptions ( @@ -259,4 +260,22 @@ AddKeyOrHashAsTrustedOrUntrusted ( BOOLEAN Trust ); +EFI_STATUS +CreateSigList ( + IN VOID *Data, + IN UINTN Size, + IN EFI_GUID *SigType, + OUT EFI_SIGNATURE_LIST **SigList + ); + +BOOLEAN +IsDbEmpty ( + VOID + ); + +EFI_STATUS +FinalizeSvBootProvisioning ( + VOID + ); + #endif diff --git a/DasharoModulePkg/Application/SovereignBootWizard/SovereignBootWizard.inf b/DasharoModulePkg/Application/SovereignBootWizard/SovereignBootWizard.inf index c397ec7621..e111f6aa42 100644 --- a/DasharoModulePkg/Application/SovereignBootWizard/SovereignBootWizard.inf +++ b/DasharoModulePkg/Application/SovereignBootWizard/SovereignBootWizard.inf @@ -65,10 +65,14 @@ [Guids] gSovereignBootWizardFormSetGuid + gEfiCertRsa2048Guid + gEfiCertPkcs7Guid + gEfiCertX509Sha256Guid + gEfiImageSecurityDatabaseGuid + gEfiGlobalVariableGuid [Protocols] ## PRODUCES # SovereignBootWizardFormSet - ## PRODUCES # SovereignBootWizardInventory gEfiDevicePathProtocolGuid gEfiHiiStringProtocolGuid ## CONSUMES gEfiHiiConfigRoutingProtocolGuid ## CONSUMES From 52b28a6a64e873a02c2cb566a954113e6a28eb9a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20=C5=BBygowski?= Date: Mon, 28 Jul 2025 14:43:40 +0200 Subject: [PATCH 14/28] UefiBootManagerLib: Pass BootCurrent to SV Boot Wizard MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Michał Żygowski --- DasharoModulePkg/Include/Guid/SovereignBoot.h | 2 ++ .../Library/UefiBootManagerLib/BmBoot.c | 19 ++++++++++++++++++- 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/DasharoModulePkg/Include/Guid/SovereignBoot.h b/DasharoModulePkg/Include/Guid/SovereignBoot.h index e84eaeae48..eafff0030a 100644 --- a/DasharoModulePkg/Include/Guid/SovereignBoot.h +++ b/DasharoModulePkg/Include/Guid/SovereignBoot.h @@ -35,6 +35,8 @@ extern EFI_GUID gSovereignBootWizardFormSetGuid; // Data passed from firmware via EFI variables (volatile, BS access) typedef struct { UINT8 AppLaunchCause; + UINT8 Pad; + UINT16 BootCurrent; } SOVEREIGN_BOOT_WIZARD_CONFIG_DATA; // State of SV Boot in EFI variables (non-volatile, BS access) diff --git a/MdeModulePkg/Library/UefiBootManagerLib/BmBoot.c b/MdeModulePkg/Library/UefiBootManagerLib/BmBoot.c index 016a0dad80..70a68022a7 100644 --- a/MdeModulePkg/Library/UefiBootManagerLib/BmBoot.c +++ b/MdeModulePkg/Library/UefiBootManagerLib/BmBoot.c @@ -1862,6 +1862,11 @@ EfiBootManagerLaunchSovereignBootWizard ( EFI_BOOT_MANAGER_LOAD_OPTION BootOption; EFI_DEVICE_PATH_PROTOCOL *FilePath; SOVEREIGN_BOOT_WIZARD_CONFIG_DATA SvBootData; + UINT16 *BootCurrent; + UINTN BootCurrentSize; + + BootCurrent = NULL; + BootCurrentSize = 0; if (!FixedPcdGetBool (PcdSovereignBootEnabled)) { return EFI_NOT_FOUND; @@ -1874,7 +1879,7 @@ EfiBootManagerLaunchSovereignBootWizard ( Status = EfiBootManagerInitializeLoadOption ( &BootOption, - 0, + LoadOptionNumberUnassigned, LoadOptionTypeBoot, LOAD_OPTION_ACTIVE | LOAD_OPTION_CATEGORY_APP, L"Sovereign Boot Wizard", @@ -1885,6 +1890,18 @@ EfiBootManagerLaunchSovereignBootWizard ( if (!EFI_ERROR (Status)) { gST->ConOut->ClearScreen (gST->ConOut); + + if (AppLaunchCause == SV_BOOT_LAUNCH_IMAGE_VERIFICATION_FAILED) { + Status = GetEfiGlobalVariable2 (L"BootCurrent", (VOID **)&BootCurrent, &BootCurrentSize); + if (!EFI_ERROR (Status) && + (BootCurrent != NULL) && + (BootCurrentSize == sizeof (UINT16))) { + SvBootData.BootCurrent = *BootCurrent; + FreePool (BootCurrent); + } else { + return Status; + } + } // // Set the Sovereign Boot Wizard launch cause // From d76d1319b0f695cb8534e5cf017e28a584a9afcb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20=C5=BBygowski?= Date: Mon, 28 Jul 2025 14:45:41 +0200 Subject: [PATCH 15/28] DMP/Application/SovereignBootWizard: Fix booting bootloaders and cert trusting MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Michał Żygowski --- .../SovereignBootWizard/BootOptionParsing.c | 8 ++ .../SovereignBootWizard/KeyManagement.c | 43 ++++---- .../SovereignBootWizard/SignatureParsing.c | 42 ++++++-- .../SovereignBootWizard/SovereignBootWizard.c | 102 ++++++++++++++++-- .../SovereignBootWizard/SovereignBootWizard.h | 3 +- 5 files changed, 156 insertions(+), 42 deletions(-) diff --git a/DasharoModulePkg/Application/SovereignBootWizard/BootOptionParsing.c b/DasharoModulePkg/Application/SovereignBootWizard/BootOptionParsing.c index 567a8f02a0..77a986d6e2 100644 --- a/DasharoModulePkg/Application/SovereignBootWizard/BootOptionParsing.c +++ b/DasharoModulePkg/Application/SovereignBootWizard/BootOptionParsing.c @@ -234,6 +234,14 @@ GetBootOptions ( continue; } + if (Private->ConfigData.AppLaunchCause == SV_BOOT_LAUNCH_IMAGE_VERIFICATION_FAILED) { + if (BootOrderList[Index] != Private->ConfigData.BootCurrent) { + DEBUG ((DEBUG_INFO, "Current boot option %u not BootCurrent (%u)", + BootOrderList[Index], Private->ConfigData.BootCurrent)); + continue; + } + } + UnicodeSPrint (BootString, sizeof (BootString), L"Boot%04x", BootOrderList[Index]); // // Get all loadoptions from the VAR diff --git a/DasharoModulePkg/Application/SovereignBootWizard/KeyManagement.c b/DasharoModulePkg/Application/SovereignBootWizard/KeyManagement.c index a5c3de376c..d62663fdbe 100644 --- a/DasharoModulePkg/Application/SovereignBootWizard/KeyManagement.c +++ b/DasharoModulePkg/Application/SovereignBootWizard/KeyManagement.c @@ -307,7 +307,7 @@ EnrollHashToSigDB ( } else { SigDBSize += sizeof (EFI_CERT_X509_SHA256); // If enrolling certificate hash to DBX, set to revoke always (keep revocation time 0). - SetMem (&SigCertHashData.TimeOfRevocation, 0, sizeof(SigCertHashData.TimeOfRevocation)); + SetMem (&SigCertHashData.TimeOfRevocation, sizeof(EFI_TIME), 0); CopyMem (&SigCertHashData.ToBeSignedHash, CertificateEntry->CertDigest, CertificateEntry->CertDigestSize); Status = CreateSigList ( &SigCertHashData, @@ -392,7 +392,7 @@ EnrollHashToSigDB ( ); } while (Key.UnicodeChar != CHAR_CARRIAGE_RETURN); - Status = EFI_SUCCESS; + Status = EFI_ABORTED; goto ON_EXIT; } } @@ -432,9 +432,14 @@ EnrollHashToSigDB ( if (EFI_ERROR (Status)) { DEBUG ((DEBUG_ERROR, "Failed to write the %s variable: %r\n", VariableName, Status)); } else { - // If image is unsigned we have to increment the bootloader index to show - // next one. Otherwise move to next certificate. - if (!SecurityContext->ImageIsSigned) { + if (Trust) { + mFirstTrustedBootloader = (INTN)mBootloaderIndex; + } + // If image is unsigned or added as untrusted we have to increment the + // bootloader index to show next one. No point in displaying other + // signatures if one of them is already in DBX, as the image will not + // pass Secure Boot verification. Otherwise move to next certificate. + if (!SecurityContext->ImageIsSigned || !Trust) { mBootloaderIndex++; mCertIndex = 0; DEBUG ((DEBUG_INFO, "Moving to next bootloader %u\n", mBootloaderIndex)); @@ -499,16 +504,13 @@ AddKeyOrHashAsTrustedOrUntrusted ( ); if (UserSelection == EfiHiiPopupSelectionYes) { // Add key or hash to DB or DBX - if (Trust) { - Status = EnrollHashToSigDB (PrivateData, EFI_IMAGE_SECURITY_DATABASE); - if (!EFI_ERROR (Status) && (mFirstTrustedBootloader == -1)) { - mFirstTrustedBootloader = (INTN)mBootloaderIndex; - } - } else { - Status = EnrollHashToSigDB (PrivateData, EFI_IMAGE_SECURITY_DATABASE1); - } - if (EFI_ERROR (Status)) { + Status = EnrollHashToSigDB ( + PrivateData, + Trust ? EFI_IMAGE_SECURITY_DATABASE : EFI_IMAGE_SECURITY_DATABASE1 + ); + + if (EFI_ERROR (Status) && Status != EFI_ABORTED) { UnicodeSPrint ( ErrorMessage, (MAXIMUM_VALUE_CHARACTERS + 8) * sizeof (CHAR16), @@ -572,7 +574,7 @@ EnrollEphemeralPk ( return EFI_DEVICE_ERROR; } - DEBUG ((DEBUG_ERROR, "PK generation done. Contrsuting signature list and enroling it\n")); + DEBUG ((DEBUG_ERROR, "PK generation done. Constructing signature list and enroling it\n")); KeySize = KeySize / 8; SigListSize = sizeof (EFI_SIGNATURE_LIST) + sizeof (EFI_SIGNATURE_DATA) - 1 + KeySize; @@ -590,7 +592,7 @@ EnrollEphemeralPk ( SigData = (EFI_SIGNATURE_DATA *)((UINT8 *)SigList + sizeof (EFI_SIGNATURE_LIST)); CopyGuid(&SigData->SignatureOwner, &gSovereignBootWizardFormSetGuid); - if (!RsaGetKey(RsaCtx, RsaKeyN, SigData->SignatureData, &KeySize)) { + if (!RsaGetKey(RsaCtx, RsaKeyN, &SigData->SignatureData[0], &KeySize)) { DEBUG ((DEBUG_ERROR, "Failed toget RSA public key modulus\n")); Status = EFI_OUT_OF_RESOURCES; goto ON_EXIT; @@ -702,15 +704,6 @@ FinalizeSvBootProvisioning ( return EFI_ABORTED; } - CreatePopUp ( - EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE, - NULL, - L"", - L"Generating ephemeral key, it may take a while...", - L"", - NULL - ); - Status = EnrollEphemeralPk (); if (EFI_ERROR (Status)) { CreatePopUp ( diff --git a/DasharoModulePkg/Application/SovereignBootWizard/SignatureParsing.c b/DasharoModulePkg/Application/SovereignBootWizard/SignatureParsing.c index 807306ce91..7c08e3c43c 100644 --- a/DasharoModulePkg/Application/SovereignBootWizard/SignatureParsing.c +++ b/DasharoModulePkg/Application/SovereignBootWizard/SignatureParsing.c @@ -203,6 +203,8 @@ FillCertificateEntries ( NewCertEntry->Signature = SOVEREIGN_BOOT_CERT_ENTRY_SIGNATURE; // Obtain the signer's certificate + // TODO: we need to obtain full cert chain and locate CA. + // Use Pkcs7GetCertificateList for that. if (!Pkcs7GetSigners (AuthData, AuthDataSize, &CertBuffer, &BufferLength, &TrustedCert, &TrustedCertLength)) { @@ -260,7 +262,6 @@ FillCertificateEntries ( NewCertEntry->CertDataSize, &IsFound ); - DEBUG ((DEBUG_INFO, "IsSignatureFoundInDatabase: %u, %r\n", IsFound, Status)); if (!EFI_ERROR (Status) && IsFound) { NewCertEntry->CertIsInDb = TRUE; } else { @@ -268,19 +269,23 @@ FillCertificateEntries ( } NewCertEntry->CertIsInDbx = IsForbiddenByDbx (AuthData, AuthDataSize, ImageDigest, ImageDigestSize); - // TODO, doesn't work. Iterate over whole EFI_CERT_STACK of CertBuffer? - NewCertEntry->ImageIsVerified = AuthenticodeVerify ( + // TODO, doesn't work. Iterate over whole EFI_CERT_STACK of CertBuffer to locate CA as trusted cert + NewCertEntry->SignatureValid = AuthenticodeVerify ( AuthData, AuthDataSize, TrustedCert, TrustedCertLength, ImageDigest, ImageDigestSize); + // Mark the image as unverified if it is in DBX. + if (NewCertEntry->CertIsInDbx) { + SecCtx->ImageIsVerified = FALSE; + } DEBUG ((DEBUG_INFO, "Certificate Details:\n" - " ImageIsVerified: %u\n" + " SignatureValid: %u\n" " CertIsInDb: %u\n" " CertIsInDbx: %u\n" " CertIsMicrosoft: %u\n", - NewCertEntry->ImageIsVerified, + NewCertEntry->SignatureValid, NewCertEntry->CertIsInDb, NewCertEntry->CertIsInDbx, NewCertEntry->CertIsMicrosoft)); @@ -391,6 +396,9 @@ FillSecurityContext ( } SecCtx->ImageIsSigned = TRUE; + // Assume verified. If anything goes wrong with certificate parsing, it will + // be set to FALSE in FillCertificateEntries. + SecCtx->ImageIsVerified = TRUE; // Parse certificates InitializeListHead (&SecCtx->Certs); @@ -402,11 +410,13 @@ FillSecurityContext ( " ImageIsInDbx: %u\n" " ImageIsInDb: %u\n" " ImageIsSigned: %u\n" + " ImageIsVerified: %u\n" " AuthenticationStatus: %u\n" " NumCertificates: %u\n", SecCtx->ImageIsInDbx, SecCtx->ImageIsInDb, SecCtx->ImageIsSigned, + SecCtx->ImageIsVerified, SecCtx->AuthenticationStatus, SecCtx->NumCertificates)); @@ -601,9 +611,11 @@ UpdateCertInfo ( Private->FormData.ImageUnsigned = (!SecurityContext->ImageIsSigned || (SecurityContext->NumCertificates == 0)); - if (SecurityContext->ImageIsInDb || SecurityContext->ImageIsInDbx) { - DEBUG ((DEBUG_INFO, "Bootloader %u already (un)trusted\n", OptionNumber)); - return EFI_NO_MEDIA; + if (Private->ConfigData.AppLaunchCause != SV_BOOT_LAUNCH_IMAGE_VERIFICATION_FAILED) { + if (SecurityContext->ImageIsInDb || SecurityContext->ImageIsInDbx) { + DEBUG ((DEBUG_INFO, "Bootloader %u already (un)trusted\n", OptionNumber)); + return EFI_NO_MEDIA; + } } // Image is unsigned? Show its hash instead of certificates @@ -622,6 +634,17 @@ UpdateCertInfo ( return EFI_SUCCESS; } + if (Private->ConfigData.AppLaunchCause != SV_BOOT_LAUNCH_IMAGE_VERIFICATION_FAILED) { + // Do not show images that are not verified (one of the certs in the + // signatures is unstrusted/in DBX), because we won't be able to boot it + // anyways. + if (!SecurityContext->ImageIsVerified) { + DEBUG ((DEBUG_INFO, "Image %u already untrusted\n", mBootloaderIndex)); + mCertIndex = 0; + return EFI_NO_MEDIA; + } + } + // Image is signed, show its certificate(s) while (mCertIndex < SecurityContext->NumCertificates) { CertificateEntry = GetCertEntry(BootloaderEntry, mCertIndex); @@ -632,9 +655,8 @@ UpdateCertInfo ( // Do not show already trusted/utrusted or microsoft certificates if (CertificateEntry->CertIsInDb || - CertificateEntry->CertIsInDbx || CertificateEntry->CertIsMicrosoft) { - DEBUG ((DEBUG_INFO, "Certificate %u already (un)trusted or belongs to Microsoft\n", mCertIndex)); + DEBUG ((DEBUG_INFO, "Certificate %u already trusted or belongs to Microsoft\n", mCertIndex)); mCertIndex++; continue; } diff --git a/DasharoModulePkg/Application/SovereignBootWizard/SovereignBootWizard.c b/DasharoModulePkg/Application/SovereignBootWizard/SovereignBootWizard.c index 6f01a2dee1..cc644be517 100644 --- a/DasharoModulePkg/Application/SovereignBootWizard/SovereignBootWizard.c +++ b/DasharoModulePkg/Application/SovereignBootWizard/SovereignBootWizard.c @@ -221,15 +221,20 @@ BootTheBootloader ( SV_MENU_ENTRY *BootloaderEntry; SV_LOAD_CONTEXT *BootloaderContext; EFI_BOOT_MANAGER_LOAD_OPTION BootOption; + EFI_BOOT_MANAGER_LOAD_OPTION *BootOptions; + UINTN BootOptionCount; + INTN OptionIndex; EFI_STATUS Status; BootloaderEntry = GetMenuEntry (&BootOptionMenu, BootloaderIndex); if (BootloaderEntry == NULL) { + DEBUG ((DEBUG_INFO, "Bootloader %u entry not found\n", BootloaderIndex)); return EFI_NO_MEDIA; } BootloaderContext = (SV_LOAD_CONTEXT *)BootloaderEntry->VariableContext; if (BootloaderContext == NULL) { + DEBUG ((DEBUG_INFO, "Bootloader %u load context not found\n", BootloaderIndex)); return EFI_NO_MEDIA; } @@ -238,7 +243,7 @@ BootTheBootloader ( LoadOptionNumberUnassigned, LoadOptionTypeBoot, LOAD_OPTION_ACTIVE, - BootloaderContext->Description, + &BootloaderContext->Description[StrLen(L"Description: ")], BootloaderContext->FilePath, BootloaderContext->OptionalData, BootloaderContext->OptionalDataSize @@ -249,15 +254,39 @@ BootTheBootloader ( return Status; } + BootOptions = EfiBootManagerGetLoadOptions ( + &BootOptionCount, + LoadOptionTypeBoot + ); + + OptionIndex = EfiBootManagerFindLoadOption ( + &BootOption, + BootOptions, + BootOptionCount + ); + if (gST->ConOut != NULL) { gST->ConOut->ClearScreen (gST->ConOut); } // TODO: Make this bootloader the first boot priority + if (OptionIndex == -1) { + Status = EfiBootManagerAddLoadOptionVariable (&BootOption, MAX_UINTN); + if (EFI_ERROR (Status)) { + EfiBootManagerFreeLoadOption (&BootOption); + EfiBootManagerFreeLoadOptions (BootOptions, BootOptionCount); + DEBUG ((DEBUG_ERROR, "Failed to add load option variable: %r\n", Status)); + return Status; + } + DEBUG ((DEBUG_INFO, "Booting %s\n", &BootloaderContext->Description[StrLen(L"Description: ")])); + EfiBootManagerBoot (&BootOption); + } else { + DEBUG ((DEBUG_INFO, "Booting %s\n", &BootloaderContext->Description[StrLen(L"Description: ")])); + EfiBootManagerBoot (&BootOptions[OptionIndex]); + } - DEBUG ((DEBUG_INFO, "Booting %s\n", BootloaderContext->Description)); - EfiBootManagerBoot (&BootOption); EfiBootManagerFreeLoadOption (&BootOption); + EfiBootManagerFreeLoadOptions (BootOptions, BootOptionCount); return EFI_SUCCESS; } @@ -301,6 +330,7 @@ Callback ( UINTN BufferSize; SOVEREIGN_BOOT_WIZARD_NV_CONFIG SvConfig; BROWSER_SETTING_SCOPE Scope; + UINTN BootloaderToBoot; if (((Value == NULL) && (Action != EFI_BROWSER_ACTION_FORM_OPEN) && (Action != EFI_BROWSER_ACTION_FORM_CLOSE)) || (ActionRequest == NULL)) @@ -412,6 +442,15 @@ Callback ( if (mBootloadersInitted) { Status = UpdateBootloaderPage (PrivateData); if (Status == EFI_NO_MEDIA) { + // If we failed image verification and do not trust the image, simply exit + if (QuestionId == DO_NOT_TRUST_KEY_FORM2_QUESTION_ID && + PrivateData->ConfigData.AppLaunchCause == SV_BOOT_LAUNCH_IMAGE_VERIFICATION_FAILED) { + PrivateData->FormBrowserEx2->SetScope (SystemLevel); + Status = PrivateData->FormBrowserEx2->ExecuteAction(BROWSER_ACTION_EXIT, 0); + + *ActionRequest = EFI_BROWSER_ACTION_REQUEST_EXIT; + return Status; + } do { CreatePopUp ( EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE, @@ -520,11 +559,36 @@ Callback ( } break; case TRUST_KEY_AND_BOOT_FORM2_QUESTION_ID: + BootloaderToBoot = mBootloaderIndex; // Add cert or image hash to DB Status = AddKeyOrHashAsTrustedOrUntrusted(PrivateData, TRUE); if (EFI_ERROR (Status)) { + // If we are already provisioned and fail, simply exit the wizard. + // If the image verification fails, a string will be shown on the + // screen by the boot manager. + if (PrivateData->NvConfig.SvBootProvisioned) { + if (PrivateData->ConfigData.AppLaunchCause == SV_BOOT_LAUNCH_VIA_SETUP) { + Scope = FormSetLevel; + } else { + Scope = SystemLevel; + } + PrivateData->FormBrowserEx2->SetScope (Scope); + Status = PrivateData->FormBrowserEx2->ExecuteAction(BROWSER_ACTION_EXIT, 0); + + *ActionRequest = EFI_BROWSER_ACTION_REQUEST_EXIT; + } return Status; } + // If we failed image verification and decided to trust the image, simply boot it + if (PrivateData->ConfigData.AppLaunchCause == SV_BOOT_LAUNCH_IMAGE_VERIFICATION_FAILED) { + Status = BootTheBootloader (PrivateData, BootloaderToBoot); + PrivateData->FormBrowserEx2->SetScope (SystemLevel); + Status = PrivateData->FormBrowserEx2->ExecuteAction(BROWSER_ACTION_EXIT, 0); + + *ActionRequest = EFI_BROWSER_ACTION_REQUEST_EXIT; + return Status; + } + // All is left here is to enroll PK to enable Secure Boot, set // Sovereign Boot to provisioned and boot. Status = FinalizeSvBootProvisioning (); @@ -538,7 +602,18 @@ Callback ( L"", NULL ); - Status = BootTheBootloader (PrivateData, mBootloaderIndex); + gBS->Stall (2 * 1000 * 1000); + Status = BootTheBootloader (PrivateData, BootloaderToBoot); + // Do not go back to wizard after booting + if (PrivateData->ConfigData.AppLaunchCause == SV_BOOT_LAUNCH_VIA_SETUP) { + Scope = FormSetLevel; + } else { + Scope = SystemLevel; + } + PrivateData->FormBrowserEx2->SetScope (Scope); + Status = PrivateData->FormBrowserEx2->ExecuteAction(BROWSER_ACTION_EXIT, 0); + + *ActionRequest = EFI_BROWSER_ACTION_REQUEST_EXIT; } else { // Unexpected failure when finalizing the provisioning, reset // bootloader index and go back to welcome form @@ -645,8 +720,8 @@ SovereignBootWizardInit ( EFI_CONFIG_KEYWORD_HANDLER_PROTOCOL *HiiKeywordHandler; EFI_HII_POPUP_PROTOCOL *PopupHandler; UINTN BufferSize; - SOVEREIGN_BOOT_WIZARD_CONFIG_DATA *ConfigData; - SOVEREIGN_BOOT_WIZARD_NV_CONFIG *SvConfig; + SOVEREIGN_BOOT_WIZARD_CONFIG_DATA *ConfigData; + SOVEREIGN_BOOT_WIZARD_NV_CONFIG *SvConfig; EFI_BOOT_MODE BootMode; EFI_INPUT_KEY HotKey; EDKII_FORM_BROWSER_EXTENSION2_PROTOCOL *FormBrowserEx2; @@ -833,6 +908,13 @@ SovereignBootWizardInit ( BufferSize = sizeof (SOVEREIGN_BOOT_WIZARD_CONFIG_DATA); Status = gRT->GetVariable (mSvBootDataVarName, &gSovereignBootWizardFormSetGuid, NULL, &BufferSize, ConfigData); if (EFI_ERROR (Status)) { + // Ensure the variable is set if there was an error reading it. + gRT->SetVariable ( + mSvBootDataVarName, + &gSovereignBootWizardFormSetGuid, + EFI_VARIABLE_BOOTSERVICE_ACCESS, + BufferSize, + ConfigData); // Unknown launch cause, try to determine if it is first launch or not if (!SvConfig->SvBootProvisioned || BootMode == BOOT_WITH_DEFAULT_SETTINGS || @@ -850,6 +932,12 @@ SovereignBootWizardInit ( } } + // If the variable was not set properly, the wizard was probably launched by + // mistake. The correct path to launch the wizard always sets the variable. + if (ConfigData->AppLaunchCause == SV_BOOT_LAUNCH_UNDEFINED) { + return SovereignBootWizardUnload (ImageHandle); + } + // Handle invalid value if (ConfigData->AppLaunchCause >= SV_BOOT_LAUNCH_MAX) { ConfigData->AppLaunchCause = SV_BOOT_LAUNCH_BOOT_WITH_DEFAULT_SETTINGS; @@ -981,6 +1069,8 @@ SovereignBootWizardUnload ( } } + // TODO: Free all pools from certificate, bootloader contexts and entries + FreePool (mPrivateData); mPrivateData = NULL; diff --git a/DasharoModulePkg/Application/SovereignBootWizard/SovereignBootWizard.h b/DasharoModulePkg/Application/SovereignBootWizard/SovereignBootWizard.h index a18ff88f3c..d98c4efa08 100644 --- a/DasharoModulePkg/Application/SovereignBootWizard/SovereignBootWizard.h +++ b/DasharoModulePkg/Application/SovereignBootWizard/SovereignBootWizard.h @@ -169,7 +169,7 @@ typedef struct { BOOLEAN CertIsInDbx; BOOLEAN CertIsInDb; - BOOLEAN ImageIsVerified; + BOOLEAN SignatureValid; BOOLEAN CertIsMicrosoft; // Placed exactly at offset 8 for alignment @@ -192,6 +192,7 @@ typedef struct { BOOLEAN ImageIsInDbx; BOOLEAN ImageIsInDb; BOOLEAN ImageIsSigned; + BOOLEAN ImageIsVerified; UINT32 AuthenticationStatus; UINT32 NumCertificates; From c45df596953dd48a7e8f91e376cd54a03c31b3a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20=C5=BBygowski?= Date: Tue, 29 Jul 2025 12:31:16 +0200 Subject: [PATCH 16/28] CryptoPkg/Library/BaseCryptLib/Pk/CryptX509.c: Implement X509GetIssuerCommonName MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Michał Żygowski --- CryptoPkg/Library/BaseCryptLib/Pk/CryptX509.c | 186 ++++++++++++++++++ 1 file changed, 186 insertions(+) diff --git a/CryptoPkg/Library/BaseCryptLib/Pk/CryptX509.c b/CryptoPkg/Library/BaseCryptLib/Pk/CryptX509.c index 0239fee4e1..2671f7b3e4 100644 --- a/CryptoPkg/Library/BaseCryptLib/Pk/CryptX509.c +++ b/CryptoPkg/Library/BaseCryptLib/Pk/CryptX509.c @@ -1157,6 +1157,192 @@ X509GetIssuerName ( return Status; } + +/** + Retrieve a string from one X.509 certificate issuer base on the Request_NID. + + @param[in] Cert Pointer to the DER-encoded X509 certificate. + @param[in] CertSize Size of the X509 certificate in bytes. + @param[in] Request_NID NID of string to obtain + @param[out] CommonName Buffer to contain the retrieved certificate issuer common + name string (UTF8). At most CommonNameSize bytes will be + written and the string will be null terminated. May be + NULL in order to determine the size buffer needed. + @param[in,out] CommonNameSize The size in bytes of the CommonName buffer on input, + and the size of buffer returned CommonName on output. + If CommonName is NULL then the amount of space needed + in buffer (including the final null) is returned. + + @retval RETURN_SUCCESS The certificate issuer CommonName retrieved successfully. + @retval RETURN_INVALID_PARAMETER If Cert is NULL. + If CommonNameSize is NULL. + If CommonName is not NULL and *CommonNameSize is 0. + If Certificate is invalid. + @retval RETURN_NOT_FOUND If no NID Name entry exists. + @retval RETURN_BUFFER_TOO_SMALL If the CommonName is NULL. The required buffer size + (including the final null) is returned in the + CommonNameSize parameter. + @retval RETURN_UNSUPPORTED The operation is not supported. + +**/ +STATIC +RETURN_STATUS +InternalX509GetIssuerNIDName ( + IN CONST UINT8 *Cert, + IN UINTN CertSize, + IN INT32 Request_NID, + OUT CHAR8 *CommonName OPTIONAL, + IN OUT UINTN *CommonNameSize + ) +{ + RETURN_STATUS ReturnStatus; + BOOLEAN Status; + X509 *X509Cert; + X509_NAME *X509Name; + INT32 Index; + INTN Length; + X509_NAME_ENTRY *Entry; + ASN1_STRING *EntryData; + UINT8 *UTF8Name; + + ReturnStatus = RETURN_INVALID_PARAMETER; + UTF8Name = NULL; + + // + // Check input parameters. + // + if ((Cert == NULL) || (CertSize > INT_MAX) || (CommonNameSize == NULL)) { + return ReturnStatus; + } + + if ((CommonName != NULL) && (*CommonNameSize == 0)) { + return ReturnStatus; + } + + X509Cert = NULL; + // + // Read DER-encoded X509 Certificate and Construct X509 object. + // + Status = X509ConstructCertificate (Cert, CertSize, (UINT8 **)&X509Cert); + if ((X509Cert == NULL) || (!Status)) { + // + // Invalid X.509 Certificate + // + goto _Exit; + } + + Status = FALSE; + + // + // Retrieve issuer name from certificate object. + // + X509Name = X509_get_issuer_name (X509Cert); + if (X509Name == NULL) { + // + // Fail to retrieve subject name content + // + goto _Exit; + } + + // + // Retrive the string from X.509 Subject base on the Request_NID + // + Index = X509_NAME_get_index_by_NID (X509Name, Request_NID, -1); + if (Index < 0) { + // + // No Request_NID name entry exists in X509_NAME object + // + *CommonNameSize = 0; + ReturnStatus = RETURN_NOT_FOUND; + goto _Exit; + } + + Entry = X509_NAME_get_entry (X509Name, Index); + if (Entry == NULL) { + // + // Fail to retrieve name entry data + // + *CommonNameSize = 0; + ReturnStatus = RETURN_NOT_FOUND; + goto _Exit; + } + + EntryData = X509_NAME_ENTRY_get_data (Entry); + + Length = ASN1_STRING_to_UTF8 (&UTF8Name, EntryData); + if (Length < 0) { + // + // Fail to convert the Name string + // + *CommonNameSize = 0; + ReturnStatus = RETURN_INVALID_PARAMETER; + goto _Exit; + } + + if (CommonName == NULL) { + *CommonNameSize = Length + 1; + ReturnStatus = RETURN_BUFFER_TOO_SMALL; + } else { + *CommonNameSize = MIN ((UINTN)Length, *CommonNameSize - 1) + 1; + CopyMem (CommonName, UTF8Name, *CommonNameSize - 1); + CommonName[*CommonNameSize - 1] = '\0'; + ReturnStatus = RETURN_SUCCESS; + } + +_Exit: + // + // Release Resources. + // + if (X509Cert != NULL) { + X509_free (X509Cert); + } + + if (UTF8Name != NULL) { + OPENSSL_free (UTF8Name); + } + + return ReturnStatus; +} + +/** + Retrieve the issuer common name (CN) string from one X.509 certificate. + + @param[in] Cert Pointer to the DER-encoded X509 certificate. + @param[in] CertSize Size of the X509 certificate in bytes. + @param[out] CommonName Buffer to contain the retrieved certificate issuer common + name string. At most CommonNameSize bytes will be + written and the string will be null terminated. May be + NULL in order to determine the size buffer needed. + @param[in,out] CommonNameSize The size in bytes of the CommonName buffer on input, + and the size of buffer returned CommonName on output. + If CommonName is NULL then the amount of space needed + in buffer (including the final null) is returned. + + @retval RETURN_SUCCESS The certificate Issuer CommonName retrieved successfully. + @retval RETURN_INVALID_PARAMETER If Cert is NULL. + If CommonNameSize is NULL. + If CommonName is not NULL and *CommonNameSize is 0. + If Certificate is invalid. + @retval RETURN_NOT_FOUND If no CommonName entry exists. + @retval RETURN_BUFFER_TOO_SMALL If the CommonName is NULL. The required buffer size + (including the final null) is returned in the + CommonNameSize parameter. + @retval RETURN_UNSUPPORTED The operation is not supported. + +**/ +RETURN_STATUS +EFIAPI +X509GetIssuerCommonName ( + IN CONST UINT8 *Cert, + IN UINTN CertSize, + OUT CHAR8 *CommonName OPTIONAL, + IN OUT UINTN *CommonNameSize + ) +{ + return InternalX509GetIssuerNIDName (Cert, CertSize, NID_commonName, CommonName, CommonNameSize); +} + + /** Retrieve the Signature Algorithm from one X.509 certificate. From 974217b3c6fd3935e4c494e800834d0f3be33d05 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20=C5=BBygowski?= Date: Tue, 29 Jul 2025 12:31:55 +0200 Subject: [PATCH 17/28] DMP/Application/SovereignBootWizard: Check cert chain and validity MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Michał Żygowski --- .../SovereignBootWizard/Asn1Time.c | 671 ++++++++++++++++++ .../SovereignBootWizard/BootOptionParsing.c | 7 +- .../SovereignBootWizard/KeyManagement.c | 100 --- .../SovereignBootWizard/SignatureParsing.c | 422 +++++++---- .../SovereignBootWizard/SovereignBootWizard.c | 23 +- .../SovereignBootWizard/SovereignBootWizard.h | 80 ++- .../SovereignBootWizard.inf | 1 + .../SovereignBootWizardHii.h | 1 + .../SovereignBootWizardVfr.vfr | 6 + .../SovereignBootWizardVfrStrings.uni | 3 +- 10 files changed, 1062 insertions(+), 252 deletions(-) create mode 100644 DasharoModulePkg/Application/SovereignBootWizard/Asn1Time.c diff --git a/DasharoModulePkg/Application/SovereignBootWizard/Asn1Time.c b/DasharoModulePkg/Application/SovereignBootWizard/Asn1Time.c new file mode 100644 index 0000000000..da995f714f --- /dev/null +++ b/DasharoModulePkg/Application/SovereignBootWizard/Asn1Time.c @@ -0,0 +1,671 @@ +/** @file +Sovereign Boot Wizard implementation. + +Copyright (c) 2025, 3mdeb Sp z o.o. All rights reserved.
+SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "SovereignBootWizard.h" + +/** + Check if it is a leap year. + + @param Time The UEFI time to be checked. + + @retval TRUE It is a leap year. + @retval FALSE It is NOT a leap year. + +**/ +STATIC +BOOLEAN +IsLeapYear ( + IN EFI_TIME *Time + ) +{ + if (Time->Year % 4 == 0) { + if (Time->Year % 100 == 0) { + if (Time->Year % 400 == 0) { + return TRUE; + } else { + return FALSE; + } + } else { + return TRUE; + } + } else { + return FALSE; + } +} + +/** + Check if the day in the UEFI time is valid. + + @param Time The UEFI time to be checked. + + @retval TRUE Valid. + @retval FALSE Invalid. + +**/ +STATIC +BOOLEAN +IsDayValid ( + IN EFI_TIME *Time + ) +{ + STATIC CONST INTN DayOfMonth[12] = { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; + + if ((Time->Day < 1) || + (Time->Day > DayOfMonth[Time->Month - 1]) || + ((Time->Month == 2) && (!IsLeapYear (Time) && (Time->Day > 28))) + ) + { + return FALSE; + } + + return TRUE; +} + +/** + Check if the time zone is valid. + Valid values are between -1440 and 1440 or 2047 (EFI_UNSPECIFIED_TIMEZONE). + + @param TimeZone The time zone to be checked. + + @retval TRUE Valid. + @retval FALSE Invalid. + +**/ +STATIC +BOOLEAN +IsValidTimeZone ( + IN INT16 TimeZone + ) +{ + return TimeZone == EFI_UNSPECIFIED_TIMEZONE || + (TimeZone >= -1440 && TimeZone <= 1440); +} + +/** + Check if the daylight is valid. + Valid values are: + 0 : Time is not affected. + 1 : Time is affected, and has not been adjusted for daylight savings. + 3 : Time is affected, and has been adjusted for daylight savings. + All other values are invalid. + + @param Daylight The daylight to be checked. + + @retval TRUE Valid. + @retval FALSE Invalid. + +**/ +STATIC +BOOLEAN +IsValidDaylight ( + IN INT8 Daylight + ) +{ + return Daylight == 0 || + Daylight == EFI_TIME_ADJUST_DAYLIGHT || + Daylight == (EFI_TIME_ADJUST_DAYLIGHT | EFI_TIME_IN_DAYLIGHT); +} + +/** + Check if the UEFI time is valid. + + @param Time The UEFI time to be checked. + + @retval TRUE Valid. + @retval FALSE Invalid. + +**/ +STATIC +BOOLEAN +IsTimeValid ( + IN EFI_TIME *Time + ) +{ + // Check the input parameters are within the range specified by UEFI + if ((Time->Year < 2000) || + (Time->Year > 2099) || + (Time->Month < 1) || + (Time->Month > 12) || + (!IsDayValid (Time)) || + (Time->Hour > 23) || + (Time->Minute > 59) || + (Time->Second > 59) || + (Time->Nanosecond > 999999999) || + (!IsValidTimeZone (Time->TimeZone)) || + (!IsValidDaylight (Time->Daylight))) + { + return FALSE; + } + + return TRUE; +} + +STATIC VOID +FormatAsn1UtcTime ( + IN OPENSSL_ASN1_TIME *Time, + IN OUT CHAR16 *DateBuffer, + IN UINTN DateBufferSize + ) +{ + ASN1_UTC_TIME *UT; + BOOLEAN HasTimezone; + BOOLEAN IsGMT; + UINT16 Year; + UINT8 Seconds; + ASN1_TIMEZONE *TZ; + UINTN Offset; + + HasTimezone = FALSE; + IsGMT = FALSE; + Seconds = 0; + TZ = NULL; + Offset = 0; + + UT = Time->Data.UtcTime; + Year = (UINT16)(UT->Year[0] - '0') * 10 + (UT->Year[1] - '0'); + + if (Year > 49) { + Year += 1900; + } else { + Year += 2000; + } + + if (UT->Seconds[0] >= '0' && UT->Seconds[0] <= '9') { + Seconds = (UT->Seconds[0] - '0') * 10 + (UT->Seconds[1] - '0'); + } + + if (*(CHAR8 *)((VOID *)UT + Time->Length - 1) == 'Z') { + IsGMT = TRUE; + } + + if (!IsGMT) { + if ((UT->Seconds[0] == '+') || (UT->Seconds[0] == '-')) { + HasTimezone = TRUE; + TZ = (ASN1_TIMEZONE *)&UT->Seconds[0]; + } + + if (!HasTimezone) { + TZ = (ASN1_TIMEZONE *)(UT + 1); + if ((UINTN)TZ + sizeof(TZ) <= ((UINTN)UT + (UINTN)Time->Length)) { + if ((TZ->Sign == '+') || (TZ->Sign != '-')) { + HasTimezone = TRUE; + } + } + } + + if (HasTimezone) { + if ((UINTN)TZ + sizeof(TZ) > ((UINTN)UT + (UINTN)Time->Length)) { + HasTimezone = FALSE; + DEBUG ((DEBUG_INFO, "UTC Time zone out of buffer bounds\n")); + } + } + } + + SetMem (DateBuffer, sizeof (DateBuffer), 0); + Offset += UnicodeSPrint ( + DateBuffer, + DateBufferSize, + L"%04u-%c%c-%c%c %c%c:%c%c:%02u", + Year, UT->Month[0], UT->Month[1], + UT->Day[0], UT->Day[1], + UT->Hour[0], UT->Hour[1], + UT->Minute[0], UT->Minute[1], + Seconds); + + if (HasTimezone) { + UnicodeSPrint ( + &DateBuffer[Offset], + DateBufferSize - Offset * sizeof (CHAR16), + L" UTC%c%c%c:%c%c", + TZ->Sign, + TZ->Hour[0], TZ->Hour[1], + TZ->Minute[0], TZ->Minute[1]); + } else { + UnicodeSPrint ( + &DateBuffer[Offset], + DateBufferSize - Offset * sizeof (CHAR16), + L" GMT"); + } +} + +STATIC VOID +FormatAsn1GeneralizedTime ( + IN OPENSSL_ASN1_TIME *Time, + IN OUT CHAR16 *DateBuffer, + IN UINTN DateBufferSize + ) +{ + ASN1_GENERALIZED_TIME *GT; + CHAR8 *Ptr; + BOOLEAN HasTimezone; + BOOLEAN IsGMT; + BOOLEAN HasFractionSeconds; + ASN1_TIMEZONE *TZ; + UINTN Offset; + INT32 Index; + UINT16 FractionSeconds; + UINT8 FractionSecondsDigits; + + HasTimezone = FALSE; + HasFractionSeconds = FALSE; + IsGMT = FALSE; + FractionSeconds = 0; + TZ = NULL; + Offset = 0; + FractionSecondsDigits = 0; + + + GT = Time->Data.GeneralizedTime; + + Ptr = (CHAR8 *)(GT + 1); + + for (Index = sizeof (ASN1_GENERALIZED_TIME); Index < Time->Length; Index++, Ptr++) { + if (*Ptr == '.') { + HasFractionSeconds = TRUE; + while (*Ptr != 'Z' && *Ptr != '+' && *Ptr != '-' && Index < Time->Length) { + FractionSeconds *= 10; + FractionSeconds = *Ptr - '0'; + FractionSecondsDigits++; + Index++; + Ptr++; + } + if (Index >= Time->Length) { + break; + } + } + + if ((*Ptr == '+') || (*Ptr == '-')) { + HasTimezone = TRUE; + TZ = (ASN1_TIMEZONE *)Ptr; + } + + if (*Ptr == 'Z') { + IsGMT = TRUE; + break; + } + } + + if (HasFractionSeconds && FractionSecondsDigits > 3) { + DEBUG ((DEBUG_INFO, "Fraction seconds incorrect format\n")); + FractionSeconds = 0; + } + + if (HasTimezone) { + if ((UINTN)TZ + sizeof(TZ) <= ((UINTN)GT + (UINTN)Time->Length)) { + if ((TZ->Sign != '+') && (TZ->Sign != '-')) { + HasTimezone = FALSE; + DEBUG ((DEBUG_INFO, "Generalized Time zone incorrect format\n")); + } + } else { + HasTimezone = FALSE; + DEBUG ((DEBUG_INFO, "Generalized Time zone out of buffer bounds\n")); + } + } + + SetMem (DateBuffer, sizeof (DateBuffer), 0); + Offset += UnicodeSPrint ( + DateBuffer, + DateBufferSize, + L"%c%c%c%c-%c%c-%c%c %c%c:%c%c:%c%c", + GT->Year[0], GT->Year[1], GT->Year[2], GT->Year[3], + GT->Month[0], GT->Month[1], + GT->Day[0], GT->Day[1], + GT->Hour[0], GT->Hour[1], + GT->Minute[0], GT->Minute[1], + GT->Seconds[0], GT->Seconds[1]); + + if (HasFractionSeconds && (FractionSeconds != 0)) { + if (FractionSecondsDigits < 3) { + while (FractionSecondsDigits > 0) { + FractionSeconds *= 10; + FractionSecondsDigits--; + } + + } + Offset += UnicodeSPrint ( + &DateBuffer[Offset], + DateBufferSize - Offset * sizeof (CHAR16), + L".%04u", + FractionSeconds); + } + + if (HasTimezone) { + UnicodeSPrint ( + &DateBuffer[Offset], + DateBufferSize - Offset * sizeof (CHAR16), + L" UTC%c%c%c:%c%c", + TZ->Sign, + TZ->Hour[0], TZ->Hour[1], + TZ->Minute[0], TZ->Minute[1]); + } else if (IsGMT) { + UnicodeSPrint ( + &DateBuffer[Offset], + DateBufferSize - Offset * sizeof (CHAR16), + L" GMT"); + } else { + UnicodeSPrint ( + &DateBuffer[Offset], + DateBufferSize - Offset * sizeof (CHAR16), + L" (local)"); + } +} + +VOID +FormatAsn1Time ( + IN OPENSSL_ASN1_TIME *Time, + IN OUT CHAR16 *DateBuffer, + IN UINTN DateBufferSize + ) +{ + if ((Time->Type != ASN1_TYPE_UTC_TIME) && + (Time->Type != ASN1_TYPE_GENERALIZED_TIME)) { + StrCpyS (DateBuffer, DateBufferSize, L"Invalid time format"); + return; + } + + if (Time->Type == ASN1_TYPE_UTC_TIME) { + FormatAsn1UtcTime (Time, DateBuffer, DateBufferSize); + } + + if (Time->Type == ASN1_TYPE_GENERALIZED_TIME) { + FormatAsn1GeneralizedTime (Time, DateBuffer, DateBufferSize); + } +} + + +STATIC BOOLEAN +Asn1UtcTimeToEfiTime ( + IN OPENSSL_ASN1_TIME *Time, + IN OUT EFI_TIME *EfiTime + ) +{ + ASN1_UTC_TIME *UT; + BOOLEAN HasTimezone; + BOOLEAN IsGMT; + UINT16 Year; + UINT8 Seconds; + ASN1_TIMEZONE *TZ; + + HasTimezone = FALSE; + IsGMT = FALSE; + Seconds = 0; + TZ = NULL; + + UT = Time->Data.UtcTime; + Year = (UINT16)(UT->Year[0] - '0') * 10 + (UT->Year[1] - '0'); + + if (Year > 49) { + Year += 1900; + } else { + Year += 2000; + } + + if (UT->Seconds[0] >= '0' && UT->Seconds[0] <= '9') { + Seconds = (UT->Seconds[0] - '0') * 10 + (UT->Seconds[1] - '0'); + } + + if (*(CHAR8 *)((VOID *)UT + Time->Length - 1) == 'Z') { + IsGMT = TRUE; + } + + if (!IsGMT) { + if ((UT->Seconds[0] == '+') || (UT->Seconds[0] == '-')) { + HasTimezone = TRUE; + TZ = (ASN1_TIMEZONE *)&UT->Seconds[0]; + } + + if (!HasTimezone) { + TZ = (ASN1_TIMEZONE *)(UT + 1); + if ((UINTN)TZ + sizeof(TZ) <= ((UINTN)UT + (UINTN)Time->Length)) { + if ((TZ->Sign == '+') || (TZ->Sign != '-')) { + HasTimezone = TRUE; + } + } + } + + if (HasTimezone) { + if ((UINTN)TZ + sizeof(TZ) > ((UINTN)UT + (UINTN)Time->Length)) { + HasTimezone = FALSE; + DEBUG ((DEBUG_INFO, "UTC Time zone out of buffer bounds\n")); + } + } + } + + EfiTime->Year = Year; + EfiTime->Month = (UT->Month[0] - '0') * 10 + (UT->Month[1] - '0'); + EfiTime->Day = (UT->Day[0] - '0') * 10 + (UT->Day[1] - '0'); + EfiTime->Hour = (UT->Hour[0] - '0') * 10 + (UT->Hour[1] - '0'); + EfiTime->Minute = (UT->Minute[0] - '0') * 10 + (UT->Minute[1] - '0'); + EfiTime->Second = Seconds; + + if (HasTimezone) { + EfiTime->TimeZone = (TZ->Hour[0] - '0') * 1000 + (TZ->Hour[1] - '0') * 100; + EfiTime->TimeZone += (TZ->Minute[0] - '0') * 10 + (TZ->Minute[1] - '0'); + if (TZ->Sign == '-') { + EfiTime->TimeZone = -EfiTime->TimeZone; + } + } else if (IsGMT) { + EfiTime->TimeZone = 0; + } else { + EfiTime->TimeZone = EFI_UNSPECIFIED_TIMEZONE; + } + + return IsTimeValid (EfiTime); +} + +STATIC BOOLEAN +Asn1GeneralizedTimeToEfiTime ( + IN OPENSSL_ASN1_TIME *Time, + IN OUT EFI_TIME *EfiTime + ) +{ + ASN1_GENERALIZED_TIME *GT; + CHAR8 *Ptr; + BOOLEAN HasTimezone; + BOOLEAN IsGMT; + BOOLEAN HasFractionSeconds; + ASN1_TIMEZONE *TZ; + INT32 Index; + UINT32 FractionSeconds; + UINT8 FractionSecondsDigits; + + HasTimezone = FALSE; + HasFractionSeconds = FALSE; + IsGMT = FALSE; + FractionSeconds = 0; + TZ = NULL; + FractionSecondsDigits = 0; + + SetMem (EfiTime, sizeof (EFI_TIME), 0); + + GT = Time->Data.GeneralizedTime; + + Ptr = (CHAR8 *)(GT + 1); + + for (Index = sizeof (ASN1_GENERALIZED_TIME); Index < Time->Length; Index++, Ptr++) { + if (*Ptr == '.') { + HasFractionSeconds = TRUE; + while (*Ptr != 'Z' && *Ptr != '+' && *Ptr != '-' && Index < Time->Length) { + FractionSeconds *= 10; + FractionSeconds = *Ptr - '0'; + FractionSecondsDigits++; + Index++; + Ptr++; + } + if (Index >= Time->Length) { + break; + } + } + + if ((*Ptr == '+') || (*Ptr == '-')) { + HasTimezone = TRUE; + TZ = (ASN1_TIMEZONE *)Ptr; + } + + if (*Ptr == 'Z') { + IsGMT = TRUE; + break; + } + } + + if (HasFractionSeconds && FractionSecondsDigits > 3) { + DEBUG ((DEBUG_INFO, "Fraction seconds incorrect format\n")); + FractionSeconds = 0; + } + + if (HasFractionSeconds && (FractionSeconds != 0)) { + if (FractionSecondsDigits < 3) { + while (FractionSecondsDigits > 0) { + FractionSeconds *= 10; + FractionSecondsDigits--; + } + } + EfiTime->Nanosecond = FractionSeconds * 1000; + } + + if (HasTimezone) { + if ((UINTN)TZ + sizeof(TZ) <= ((UINTN)GT + (UINTN)Time->Length)) { + if ((TZ->Sign != '+') && (TZ->Sign != '-')) { + HasTimezone = FALSE; + DEBUG ((DEBUG_INFO, "Generalized Time zone incorrect format\n")); + } + } else { + HasTimezone = FALSE; + DEBUG ((DEBUG_INFO, "Generalized Time zone out of buffer bounds\n")); + } + } + + EfiTime->Year = (GT->Year[0] - '0') * 1000 + (GT->Year[1] - '0') * 100 + + (GT->Year[2] - '0') * 10 + (GT->Year[3] = '0'); + EfiTime->Month = (GT->Month[0] - '0') * 10 + (GT->Month[1] - '0'); + EfiTime->Day = (GT->Day[0] - '0') * 10 + (GT->Day[1] - '0'); + EfiTime->Hour = (GT->Hour[0] - '0') * 10 + (GT->Hour[1] - '0'); + EfiTime->Minute = (GT->Minute[0] - '0') * 10 + (GT->Minute[1] - '0'); + EfiTime->Second = (GT->Seconds[0] - '0') * 10 + (GT->Seconds[1] - '0'); + + if (HasTimezone) { + EfiTime->TimeZone = (TZ->Hour[0] - '0') * 1000 + (TZ->Hour[1] - '0') * 100; + EfiTime->TimeZone += (TZ->Minute[0] - '0') * 10 + (TZ->Minute[1] - '0'); + if (TZ->Sign == '-') { + EfiTime->TimeZone = -EfiTime->TimeZone; + } + } else if (IsGMT) { + EfiTime->TimeZone = 0; + } else { + EfiTime->TimeZone = EFI_UNSPECIFIED_TIMEZONE; + } + + return IsTimeValid (EfiTime); +} + +BOOLEAN +Asn1TimeToEfiTime ( + IN OPENSSL_ASN1_TIME *Asn1Time, + IN OUT EFI_TIME *EfiTime + ) +{ + SetMem (EfiTime, sizeof (EFI_TIME), 0); + + if (Asn1Time->Type == ASN1_TYPE_UTC_TIME) { + return Asn1UtcTimeToEfiTime (Asn1Time, EfiTime); + } + + if (Asn1Time->Type == ASN1_TYPE_GENERALIZED_TIME) { + return Asn1GeneralizedTimeToEfiTime (Asn1Time, EfiTime); + } + + return FALSE; +} + +OPENSSL_ASN1_TIME * +EfiTimeToAsn1Time ( + IN EFI_TIME *EfiTime + ) +{ + UINTN DateTimeSize; + CHAR8 TimeStr[50]; + VOID *Time; + + DateTimeSize = 0; + Time = NULL; + + if (EfiTime == NULL) { + return NULL; + } + + if (!IsTimeValid (EfiTime)) { + return NULL; + } + + SetMem (TimeStr, sizeof (TimeStr), 0); + // Print the YYYYMMDDhhmmssZ string + AsciiSPrint (TimeStr, sizeof (TimeStr), "%04u%02u%02u%02u%02u%02uZ", + EfiTime->Year, EfiTime->Month, EfiTime->Day, + EfiTime->Hour, EfiTime->Minute, EfiTime->Second); + + // Get required buffer size + X509FormatDateTime (TimeStr, NULL, &DateTimeSize); + if (DateTimeSize == 0) { + return NULL; + } + Time = AllocateZeroPool (DateTimeSize); + if (Time == NULL) { + return NULL; + } + + if (!X509FormatDateTime (TimeStr, Time, &DateTimeSize)) { + FreePool (Time); + return NULL; + } + + return (OPENSSL_ASN1_TIME *)Time; +} + +/** + Helper function to populate an EFI_TIME instance. + + @param[in] Time Pointer to the time structure + +**/ +EFI_STATUS +GetCurrentTime ( + IN EFI_TIME *Time + ) +{ + EFI_STATUS Status; + VOID *TestPointer; + + if (Time == NULL) { + return EFI_INVALID_PARAMETER; + } + + Status = gBS->LocateProtocol (&gEfiRealTimeClockArchProtocolGuid, NULL, &TestPointer); + if (EFI_ERROR (Status)) { + return Status; + } + + ZeroMem (Time, sizeof (EFI_TIME)); + Status = gRT->GetTime (Time, NULL); + if (EFI_ERROR (Status)) { + DEBUG (( + DEBUG_ERROR, + "%a(), GetTime() failed, status = '%r'\n", + __func__, + Status + )); + return Status; + } + + Time->Pad1 = 0; + Time->Nanosecond = 0; + Time->TimeZone = 0; + Time->Daylight = 0; + Time->Pad2 = 0; + + return EFI_SUCCESS; +} diff --git a/DasharoModulePkg/Application/SovereignBootWizard/BootOptionParsing.c b/DasharoModulePkg/Application/SovereignBootWizard/BootOptionParsing.c index 77a986d6e2..3940943458 100644 --- a/DasharoModulePkg/Application/SovereignBootWizard/BootOptionParsing.c +++ b/DasharoModulePkg/Application/SovereignBootWizard/BootOptionParsing.c @@ -216,6 +216,7 @@ GetBootOptions ( LoadOptionFromVar = NULL; InitializeListHead (&BootOptionMenu.Head); + DEBUG ((DEBUG_INFO, "Locating boot options\n")); // // Get the BootOrder from the Var // @@ -234,10 +235,10 @@ GetBootOptions ( continue; } + // TODO: Some boot options may not have entries returned by EfiBootManagerGetLoadOptions + // Query the Boot#### variable directly using BootCurrent index. if (Private->ConfigData.AppLaunchCause == SV_BOOT_LAUNCH_IMAGE_VERIFICATION_FAILED) { if (BootOrderList[Index] != Private->ConfigData.BootCurrent) { - DEBUG ((DEBUG_INFO, "Current boot option %u not BootCurrent (%u)", - BootOrderList[Index], Private->ConfigData.BootCurrent)); continue; } } @@ -483,7 +484,6 @@ UpdateBootloaderPage ( } Status = UpdateCertInfo (Private, mBootloaderIndex); - DEBUG ((DEBUG_INFO, "UpdateCertInfo state: %r\n", Status)); if (Status == EFI_NO_MEDIA) { DEBUG ((DEBUG_INFO, "No more keys/certificates for bootloader %u\n", mBootloaderIndex)); // No more keys/certs to show for this bootloader, proceed to the next one @@ -522,6 +522,5 @@ UpdateBootloaderPage ( HiiSetString (Private->HiiHandle, STRING_TOKEN (STR_BOOTOPT_DESCRIPTION), L"Not Found!", NULL); } - DEBUG ((DEBUG_INFO, "Bootloader state %r\n", Status)); return Status; } diff --git a/DasharoModulePkg/Application/SovereignBootWizard/KeyManagement.c b/DasharoModulePkg/Application/SovereignBootWizard/KeyManagement.c index d62663fdbe..1854deab77 100644 --- a/DasharoModulePkg/Application/SovereignBootWizard/KeyManagement.c +++ b/DasharoModulePkg/Application/SovereignBootWizard/KeyManagement.c @@ -141,51 +141,6 @@ PrepareSbVariablesForSvBoot ( return SetSecureBootMode (STANDARD_SECURE_BOOT_MODE); } -/** - Helper function to populate an EFI_TIME instance. - - @param[in] Time FileContext cached in SecureBootConfig driver - -**/ -STATIC -EFI_STATUS -GetCurrentTime ( - IN EFI_TIME *Time - ) -{ - EFI_STATUS Status; - VOID *TestPointer; - - if (Time == NULL) { - return EFI_INVALID_PARAMETER; - } - - Status = gBS->LocateProtocol (&gEfiRealTimeClockArchProtocolGuid, NULL, &TestPointer); - if (EFI_ERROR (Status)) { - return Status; - } - - ZeroMem (Time, sizeof (EFI_TIME)); - Status = gRT->GetTime (Time, NULL); - if (EFI_ERROR (Status)) { - DEBUG (( - DEBUG_ERROR, - "%a(), GetTime() failed, status = '%r'\n", - __func__, - Status - )); - return Status; - } - - Time->Pad1 = 0; - Time->Nanosecond = 0; - Time->TimeZone = 0; - Time->Daylight = 0; - Time->Pad2 = 0; - - return EFI_SUCCESS; -} - /** Creates EFI Signature List structure. @param[in] Data A pointer to signature data. @@ -261,12 +216,6 @@ EnrollHashToSigDB ( SV_SECURITY_CONTEXT *SecurityContext; SV_CERT_ENTRY *CertificateEntry; EFI_STATUS Status; - UINT8 CertValidFrom[64]; - UINTN CertValidFromLen; - UINT8 CertValidTo[64]; - UINTN CertValidToLen; - MBED_TLS_DATETIME_OBECT CurrentTime; - EFI_INPUT_KEY Key; BOOLEAN Trust; BootloaderEntry = GetMenuEntry (&BootOptionMenu, mBootloaderIndex); @@ -348,55 +297,6 @@ EnrollHashToSigDB ( goto ON_EXIT; } - // Do not allow to add expired certificate to DB - if (SecurityContext->ImageIsSigned && Trust) { - CurrentTime.Year = (INT32)Time.Year; - CurrentTime.Month = (INT32)Time.Month; - CurrentTime.Day = (INT32)Time.Day; - CurrentTime.Hour = (INT32)Time.Hour; - CurrentTime.Minute = (INT32)Time.Minute; - CurrentTime.Second = (INT32)Time.Second; - - // If enrolling to DB, check the expiry date - CertValidFromLen = 64; - CertValidToLen = 64; - if (!X509GetValidity(CertificateEntry->CertData, - CertificateEntry->CertDataSize, - CertValidFrom, - &CertValidFromLen, - CertValidTo, - &CertValidToLen)) { - DEBUG ((DEBUG_ERROR, "Could not get certificate validity\n")); - Status = EFI_NOT_FOUND; - goto ON_EXIT; - } - - if (CertValidToLen == 0 || CertValidToLen != sizeof (MBED_TLS_DATETIME_OBECT)) { - DEBUG ((DEBUG_ERROR, "Invalid certificate validity length\n")); - Status = EFI_BAD_BUFFER_SIZE; - goto ON_EXIT; - } - - if (X509CompareDateTime (&CurrentTime, CertValidTo) >= 0) { - do { - CreatePopUp ( - EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE, - &Key, - L"", - L"The certificate you want to trust has expired.", - L"Can not add it to trusted signature database.", - L"", - L"Press ENTER to abort the process...", - L"", - NULL - ); - } while (Key.UnicodeChar != CHAR_CARRIAGE_RETURN); - - Status = EFI_ABORTED; - goto ON_EXIT; - } - } - Status = CreateTimeBasedPayload (&SigDBSize, (UINT8 **)&SigDBHash, &Time); if (EFI_ERROR (Status)) { DEBUG ((DEBUG_ERROR, "Failed to create time-based data payload: %r\n", Status)); diff --git a/DasharoModulePkg/Application/SovereignBootWizard/SignatureParsing.c b/DasharoModulePkg/Application/SovereignBootWizard/SignatureParsing.c index 7c08e3c43c..56404467c9 100644 --- a/DasharoModulePkg/Application/SovereignBootWizard/SignatureParsing.c +++ b/DasharoModulePkg/Application/SovereignBootWizard/SignatureParsing.c @@ -94,6 +94,195 @@ VerifyImageHashInDatabases ( } } +STATIC BOOLEAN +CertIsValid ( + IN SV_CERT_ENTRY *CertificateEntry + ) +{ + EFI_STATUS Status; + BOOLEAN Valid; + EFI_TIME Time; + UINT8 CertValidFrom[64]; + UINTN CertValidFromLen; + UINT8 CertValidTo[64]; + UINTN CertValidToLen; + OPENSSL_ASN1_TIME *CurrentTime; + + if (CertificateEntry == NULL) { + return FALSE; + } + + Valid = TRUE; + CurrentTime = NULL; + + Status = GetCurrentTime (&Time); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "Fail to fetch valid time data: %r\n", Status)); + return FALSE; + } + + // We should skip presenting expired or not valid certificates. Adding an + // expired certificate to DB will cause verification failure. + CurrentTime = EfiTimeToAsn1Time (&Time); + if (CurrentTime == NULL) { + DEBUG ((DEBUG_ERROR, "Fail to convert time data\n")); + Valid = FALSE; + goto ON_EXIT; + } + + // If enrolling to DB, check the expiry date + CertValidFromLen = 64; + CertValidToLen = 64; + if (!X509GetValidity(CertificateEntry->CertData, + CertificateEntry->CertDataSize, + CertValidFrom, + &CertValidFromLen, + CertValidTo, + &CertValidToLen)) { + DEBUG ((DEBUG_ERROR, "Could not get certificate validity\n")); + Valid = FALSE; + goto ON_EXIT; + } + + if (X509CompareDateTime (CurrentTime, CertValidTo) >= 0) { + DEBUG ((DEBUG_INFO, "Certificate already expired\n")); + Valid = FALSE; + } + + if (X509CompareDateTime (CurrentTime, CertValidFrom) < 0) { + DEBUG ((DEBUG_INFO, "Certificate not yet valid\n")); + Valid = FALSE; + } + +ON_EXIT: + + if (CurrentTime != NULL) { + FreePool (CurrentTime); + } + + return Valid; +} + +STATIC EFI_STATUS +CreateNewCert ( + IN SV_SECURITY_CONTEXT *SecCtx, + IN UINT8 *CertData, + IN UINTN CertDataSize, + IN VOID *FileBuffer, + IN UINTN FileSize, + IN UINT8 *AuthData, + IN UINTN AuthDataSize, + IN OUT SV_CERT_ENTRY **CertEntry + ) +{ + SV_CERT_ENTRY *NewCertEntry; + EFI_STATUS Status; + EFI_GUID CertType; + UINT8 ImageDigest[MAX_DIGEST_SIZE]; + UINTN ImageDigestSize; + BOOLEAN IsFound; + UINTN Index; + + if ((SecCtx == NULL) || (CertData == NULL) || (FileBuffer == NULL) || + (AuthData == NULL) || (CertEntry == NULL) || (CertDataSize == 0) || + (FileSize == 0) || (AuthDataSize == 0)) { + DEBUG ((DEBUG_ERROR, "%a, Invalid parameter\n", __FUNCTION__)); + return EFI_INVALID_PARAMETER; + } + + NewCertEntry = (SV_CERT_ENTRY *) AllocateZeroPool (sizeof(SV_CERT_ENTRY)); + + if (NewCertEntry == NULL) { + DEBUG ((DEBUG_ERROR, "Not enough free memory for certificate entry\n")); + return EFI_OUT_OF_RESOURCES; + } + + *CertEntry = NewCertEntry; + + NewCertEntry->Signature = SOVEREIGN_BOOT_CERT_ENTRY_SIGNATURE; + NewCertEntry->CertData = AllocateCopyPool (CertDataSize, CertData); + + if (NewCertEntry->CertData == NULL) { + DEBUG ((DEBUG_ERROR, "Not enough free memory for certificate data\n")); + return EFI_OUT_OF_RESOURCES; + } + + NewCertEntry->CertDataSize = CertDataSize; + NewCertEntry->CertIsValid = CertIsValid(NewCertEntry); + + if (CalculateCertHash (NewCertEntry->CertData, NewCertEntry->CertDataSize, HASHALG_SHA256, NewCertEntry->CertDigest)) { + NewCertEntry->CertDigestSize = SHA256_DIGEST_SIZE; + CopyGuid (&NewCertEntry->CertType, &gEfiCertX509Sha256Guid); + } else { + DEBUG ((DEBUG_ERROR, "Could not calculate TBS certificate hash\n")); + return EFI_DEVICE_ERROR; + } + + Status = HashPeImageByType ( + FileBuffer, + FileSize, + AuthData, + AuthDataSize, + ImageDigest, + &ImageDigestSize, + &CertType); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "Failed to calculate image hash\n")); + return Status; + } + + // + // Verify the digital signature and check against databases + // + + // EDK2 checks the DB only against full X509 certificates + IsFound = FALSE; + Status = IsSignatureFoundInDatabase ( + EFI_IMAGE_SECURITY_DATABASE, + NewCertEntry->CertData, + &gEfiCertX509Guid, + NewCertEntry->CertDataSize, + &IsFound + ); + if (!EFI_ERROR (Status) && IsFound) { + NewCertEntry->CertIsInDb = TRUE; + } else { + NewCertEntry->CertIsInDb = FALSE; + } + + NewCertEntry->CertIsInDbx = IsForbiddenByDbx (AuthData, AuthDataSize, ImageDigest, ImageDigestSize); + NewCertEntry->SignatureValid = AuthenticodeVerify ( + AuthData, AuthDataSize, + NewCertEntry->CertData, NewCertEntry->CertDataSize, + ImageDigest, ImageDigestSize); + + // Mark the image as unverified if it is in DBX. + if (NewCertEntry->CertIsInDbx) { + SecCtx->ImageIsVerified = FALSE; + } + + DEBUG ((DEBUG_INFO, "Certificate Details:\n" + " SignatureValid: %u\n" + " CertIsInDb: %u\n" + " CertIsInDbx: %u\n" + " CertIsMicrosoft: %u\n" + " CertIsValid: %u\n", + NewCertEntry->SignatureValid, + NewCertEntry->CertIsInDb, + NewCertEntry->CertIsInDbx, + NewCertEntry->CertIsMicrosoft, + NewCertEntry->CertIsValid)); + + DEBUG ((DEBUG_INFO, "Certificate hash:\n")); + for (Index = 0; Index < NewCertEntry->CertDigestSize; Index++) { + DEBUG ((DEBUG_INFO, "%02X", NewCertEntry->CertDigest[Index])); + } + DEBUG ((DEBUG_INFO, "\n")); + + return EFI_SUCCESS; + +} + VOID FillCertificateEntries ( IN VOID *FileBuffer, @@ -110,19 +299,15 @@ FillCertificateEntries ( UINT32 Offset; UINT8 *AuthData; UINTN AuthDataSize; - EFI_STATUS HashStatus; - UINT8 ImageDigest[MAX_DIGEST_SIZE]; - UINTN ImageDigestSize; + EFI_STATUS CertStatus; SV_CERT_ENTRY *NewCertEntry; UINT8 *CertBuffer; UINTN BufferLength; - UINT8 *TrustedCert; - UINTN TrustedCertLength; + UINT8 *UnchainedCert; + UINTN UnchainedCertLength; UINTN CertCount; - EFI_GUID CertType; - UINTN Index; - EFI_STATUS Status; - BOOLEAN IsFound; + UINT8 *CertPtr; + UINT8 NumCert; CertCount = 0; // @@ -193,115 +378,103 @@ FillCertificateEntries ( continue; } - NewCertEntry = (SV_CERT_ENTRY *) AllocateZeroPool (sizeof(SV_CERT_ENTRY)); - - if (NewCertEntry == NULL) { - DEBUG ((DEBUG_ERROR, "Not enough free memory for certificate data\n")); - return; - } - - NewCertEntry->Signature = SOVEREIGN_BOOT_CERT_ENTRY_SIGNATURE; - - // Obtain the signer's certificate - // TODO: we need to obtain full cert chain and locate CA. - // Use Pkcs7GetCertificateList for that. - if (!Pkcs7GetSigners (AuthData, AuthDataSize, - &CertBuffer, &BufferLength, - &TrustedCert, &TrustedCertLength)) { - FreePool (NewCertEntry); + // Obtain the signer's certificates + if (!Pkcs7GetCertificatesList (AuthData, AuthDataSize, + &CertBuffer, &BufferLength, + &UnchainedCert, &UnchainedCertLength)) { DEBUG ((DEBUG_ERROR, "Could not get PKCS7 signers\n")); continue; } - if ((BufferLength == 0) || (CertBuffer == NULL) || ((*CertBuffer) == 0)) { - FreePool (NewCertEntry); - DEBUG ((DEBUG_ERROR, "PKCS7 signers data invalid\n")); - continue; + if ((BufferLength != 0) && (CertBuffer != NULL)) { + NumCert = CertBuffer[0]; + } else { + NumCert = 0; } - // Free unused cert chain - Pkcs7FreeSigners (CertBuffer); - - // Save the buffer to the signer's certificate - NewCertEntry->CertData = TrustedCert; - NewCertEntry->CertDataSize = TrustedCertLength; - - if (CalculateCertHash (TrustedCert, TrustedCertLength, HASHALG_SHA256, NewCertEntry->CertDigest)) { - NewCertEntry->CertDigestSize = SHA256_DIGEST_SIZE; - CopyGuid (&NewCertEntry->CertType, &gEfiCertX509Sha256Guid); + if (NumCert == 0) { + DEBUG ((DEBUG_INFO, "Chained certificates not found\n")); } else { - DEBUG ((DEBUG_ERROR, "Could not calculate TBS certificate hash\n")); + DEBUG ((DEBUG_INFO, "Found %u chained certificates\n", NumCert)); } - HashStatus = HashPeImageByType ( - FileBuffer, - FileSize, - AuthData, - AuthDataSize, - ImageDigest, - &ImageDigestSize, - &CertType); - if (EFI_ERROR (HashStatus)) { - if (TrustedCert != NULL) { - Pkcs7FreeSigners (TrustedCert); + // Skip the number of certs + CertPtr = CertBuffer + 1; + while (NumCert > 0) { + NewCertEntry = NULL; + CertStatus = CreateNewCert ( + SecCtx, + &CertPtr[4], + *(UINT32 *)CertPtr, + FileBuffer, + FileSize, + AuthData, + AuthDataSize, + &NewCertEntry + ); + if (!EFI_ERROR (CertStatus)) { + InsertTailList (&SecCtx->Certs, &NewCertEntry->CertLink); + CertCount++; + } else { + if (NewCertEntry != NULL) { + FreePool (NewCertEntry); + } } - FreePool (NewCertEntry); - DEBUG ((EFI_D_ERROR, "Failed to calculate image hash\n")); - continue; + + CertPtr += *(UINT32 *)CertPtr; // Certificate size + CertPtr += sizeof (UINT32); // Certificate size field + NumCert--; + } + if (CertBuffer != NULL) { + Pkcs7FreeSigners (CertBuffer); } - // - // Verify the digital signature and check against databases - // + if ((UnchainedCertLength != 0) && (UnchainedCert != NULL)) { + NumCert = UnchainedCert[0]; + } else { + NumCert = 0; + } - // EDK2 checks the DB only against full X509 certificates - IsFound = FALSE; - Status = IsSignatureFoundInDatabase ( - EFI_IMAGE_SECURITY_DATABASE, - NewCertEntry->CertData, - &gEfiCertX509Guid, - NewCertEntry->CertDataSize, - &IsFound - ); - if (!EFI_ERROR (Status) && IsFound) { - NewCertEntry->CertIsInDb = TRUE; + if (NumCert == 0) { + DEBUG ((DEBUG_INFO, "Unchained certificates not found\n")); } else { - NewCertEntry->CertIsInDb = FALSE; + DEBUG ((DEBUG_INFO, "Found %u unchained certificates\n", NumCert)); } - NewCertEntry->CertIsInDbx = IsForbiddenByDbx (AuthData, AuthDataSize, ImageDigest, ImageDigestSize); - // TODO, doesn't work. Iterate over whole EFI_CERT_STACK of CertBuffer to locate CA as trusted cert - NewCertEntry->SignatureValid = AuthenticodeVerify ( - AuthData, AuthDataSize, - TrustedCert, TrustedCertLength, - ImageDigest, ImageDigestSize); + // Skip the number of certs + CertPtr = UnchainedCert + 1; + while (NumCert > 0) { + NewCertEntry = NULL; + CertStatus = CreateNewCert ( + SecCtx, + &CertPtr[4], + *(UINT32 *)CertPtr, + FileBuffer, + FileSize, + AuthData, + AuthDataSize, + &NewCertEntry + ); + if (!EFI_ERROR (CertStatus)) { + InsertTailList (&SecCtx->Certs, &NewCertEntry->CertLink); + CertCount++; + } else { + DEBUG ((DEBUG_ERROR, "Failed to add new certificate: %r\n", CertStatus)); + if (NewCertEntry != NULL) { + FreePool (NewCertEntry); + } + } - // Mark the image as unverified if it is in DBX. - if (NewCertEntry->CertIsInDbx) { - SecCtx->ImageIsVerified = FALSE; + CertPtr += *(UINT32 *)CertPtr; // Certificate size + CertPtr += sizeof (UINT32); // Certificate size field + NumCert--; } - - DEBUG ((DEBUG_INFO, "Certificate Details:\n" - " SignatureValid: %u\n" - " CertIsInDb: %u\n" - " CertIsInDbx: %u\n" - " CertIsMicrosoft: %u\n", - NewCertEntry->SignatureValid, - NewCertEntry->CertIsInDb, - NewCertEntry->CertIsInDbx, - NewCertEntry->CertIsMicrosoft)); - - DEBUG ((DEBUG_INFO, "Certificate hash:\n")); - for (Index = 0; Index < NewCertEntry->CertDigestSize; Index++) { - DEBUG ((DEBUG_INFO, "%02X", NewCertEntry->CertDigest[Index])); + if (UnchainedCert != NULL) { + Pkcs7FreeSigners (UnchainedCert); } - DEBUG ((DEBUG_INFO, "\n")); - - InsertTailList (&SecCtx->Certs, &NewCertEntry->CertLink); - CertCount++; } if (Offset != SecDataDirEnd) { - DEBUG ((EFI_D_ERROR, "The Size in Certificate Table or the attribute certificate table is corrupted.\n")); + DEBUG ((DEBUG_ERROR, "The Size in Certificate Table or the attribute certificate table is corrupted.\n")); } SecCtx->NumCertificates = CertCount; @@ -653,12 +826,20 @@ UpdateCertInfo ( return EFI_NO_MEDIA; } - // Do not show already trusted/utrusted or microsoft certificates - if (CertificateEntry->CertIsInDb || - CertificateEntry->CertIsMicrosoft) { - DEBUG ((DEBUG_INFO, "Certificate %u already trusted or belongs to Microsoft\n", mCertIndex)); - mCertIndex++; - continue; + // Do not show already trusted/utrusted, invalid or microsoft certificates + if (Private->ConfigData.AppLaunchCause != SV_BOOT_LAUNCH_IMAGE_VERIFICATION_FAILED) { + if (CertificateEntry->CertIsInDb || CertificateEntry->CertIsMicrosoft) { + DEBUG ((DEBUG_INFO, "Certificate %u already trusted, or belongs to Microsoft\n", + mCertIndex)); + mCertIndex++; + continue; + } + if (!CertificateEntry->CertIsValid || !CertificateEntry->SignatureValid) { + DEBUG ((DEBUG_INFO, "Certificate %u or itssignature is invalid\n", + mCertIndex)); + mCertIndex++; + continue; + } } break; @@ -684,17 +865,17 @@ UpdateCertInfo ( } VOID -UpdateCertValidtyStrings ( +UpdateCertValidityStrings ( IN SOVEREIGN_BOOT_WIZARD_PRIVATE_DATA *Private, IN SV_CERT_ENTRY *CertificateEntry ) { - CHAR16 DateBuffer[30]; + CHAR16 DateBuffer[50]; UINT8 CertValidFrom[64]; UINTN CertValidFromLen; UINT8 CertValidTo[64]; UINTN CertValidToLen; - MBED_TLS_DATETIME_OBECT *CertValidTime; + OPENSSL_ASN1_TIME *CertTime; CertValidFromLen = 64; CertValidToLen = 64; @@ -706,33 +887,14 @@ UpdateCertValidtyStrings ( CertValidTo, &CertValidToLen)) { - CertValidTime = (MBED_TLS_DATETIME_OBECT *)CertValidTo; + CertTime = (OPENSSL_ASN1_TIME *)CertValidTo; SetMem (DateBuffer, sizeof (DateBuffer), 0); - UnicodeSPrint ( - DateBuffer, - sizeof (DateBuffer), - L"%04u-%02u-%02u %02u:%02u:%02u", - (UINT16)(CertValidTime->Year & 0xFFFF), - (UINT8)(CertValidTime->Month & 0xFF), - (UINT8)(CertValidTime->Day & 0xFF), - (UINT8)(CertValidTime->Hour & 0xFF), - (UINT8)(CertValidTime->Minute & 0xFF), - (UINT8)(CertValidTime->Second & 0xFF)); + FormatAsn1Time (CertTime, DateBuffer, sizeof (DateBuffer)); HiiSetString (Private->HiiHandle, STRING_TOKEN (STR_VALIDITY_AFTER_DATE), DateBuffer, NULL); - CertValidTime = (MBED_TLS_DATETIME_OBECT *)CertValidFrom; + CertTime = (OPENSSL_ASN1_TIME *)CertValidFrom; SetMem (DateBuffer, sizeof (DateBuffer), 0); - UnicodeSPrint ( - DateBuffer, - sizeof (DateBuffer), - L"%04u-%02u-%02u %02u:%02u:%02u", - (UINT16)(CertValidTime->Year & 0xFFFF), - (UINT8)(CertValidTime->Month & 0xFF), - (UINT8)(CertValidTime->Day & 0xFF), - (UINT8)(CertValidTime->Hour & 0xFF), - (UINT8)(CertValidTime->Minute & 0xFF), - (UINT8)(CertValidTime->Second & 0xFF)); - + FormatAsn1Time (CertTime, DateBuffer, sizeof (DateBuffer)); HiiSetString (Private->HiiHandle, STRING_TOKEN (STR_VALIDITY_BEFORE_DATE), DateBuffer, NULL); } else { @@ -909,7 +1071,7 @@ UpdateCertDetails ( return EFI_NO_MEDIA; } - UpdateCertValidtyStrings (Private, CertificateEntry); + UpdateCertValidityStrings (Private, CertificateEntry); UpdateCertIssuerAndSubjectStrings (Private, CertificateEntry); UpdateCertSerialNumberString (Private, CertificateEntry); UpdateCertKeyStrings (Private, CertificateEntry); diff --git a/DasharoModulePkg/Application/SovereignBootWizard/SovereignBootWizard.c b/DasharoModulePkg/Application/SovereignBootWizard/SovereignBootWizard.c index cc644be517..b4a55aeb37 100644 --- a/DasharoModulePkg/Application/SovereignBootWizard/SovereignBootWizard.c +++ b/DasharoModulePkg/Application/SovereignBootWizard/SovereignBootWizard.c @@ -9,13 +9,13 @@ SPDX-License-Identifier: BSD-2-Clause-Patent #include "SovereignBootWizard.h" SOVEREIGN_BOOT_WIZARD_PRIVATE_DATA *mPrivateData = NULL; -BOOLEAN mBootloadersInitted = FALSE; +BOOLEAN mBootloadersInitted; STATIC CHAR16 mSvBootDataVarName[] = SV_BOOT_DATA_VAR; STATIC CHAR16 mVarStoreName[] = L"SvBootFormData"; STATIC CHAR16 mSvBootConfigVarName[] = SV_BOOT_CONFIG_VAR; -STATIC BOOLEAN mBootloadersShown = FALSE; +STATIC BOOLEAN mBootloadersShown; STATIC HII_VENDOR_DEVICE_PATH mHiiVendorDevicePath = { { @@ -39,9 +39,9 @@ STATIC HII_VENDOR_DEVICE_PATH mHiiVendorDevicePath = { } }; -UINTN mBootloaderIndex = 0; -UINTN mCertIndex = 0; -INTN mFirstTrustedBootloader = -1; +UINTN mBootloaderIndex; +UINTN mCertIndex; +INTN mFirstTrustedBootloader; EFI_STATUS EFIAPI @@ -437,6 +437,13 @@ Callback ( *ActionRequest = EFI_BROWSER_ACTION_REQUEST_EXIT; break; + case SKIP_KEY_FORM2_QUESTION_ID: + if (PrivateData->FormData.ImageUnsigned) { + mBootloaderIndex++; + } else { + mCertIndex++; + } + // fallthrough case DO_NOT_TRUST_KEY_FORM2_QUESTION_ID: case TRUST_KEY_FORM2_QUESTION_ID: if (mBootloadersInitted) { @@ -979,6 +986,12 @@ SovereignBootWizardInit ( mPrivateData->FormBrowserEx2->RegisterHotKey (&HotKey, 0, 0, NULL); } + mBootloaderIndex = 0; + mCertIndex = 0; + mFirstTrustedBootloader = -1; + mBootloadersInitted = FALSE; + mBootloadersShown = FALSE; + if (SvConfig->SvBootProvisioned) { if (ConfigData->AppLaunchCause == SV_BOOT_LAUNCH_IMAGE_VERIFICATION_FAILED) { FormId = SOVEREIGN_BOOT_WIZARD_CONFIG_FORM_ID; diff --git a/DasharoModulePkg/Application/SovereignBootWizard/SovereignBootWizard.h b/DasharoModulePkg/Application/SovereignBootWizard/SovereignBootWizard.h index d98c4efa08..4138a0cff5 100644 --- a/DasharoModulePkg/Application/SovereignBootWizard/SovereignBootWizard.h +++ b/DasharoModulePkg/Application/SovereignBootWizard/SovereignBootWizard.h @@ -114,19 +114,53 @@ typedef struct { EFI_DEVICE_PATH_PROTOCOL End; } HII_VENDOR_DEVICE_PATH; +// YYMMDDHHMMSS Z +typedef struct { + CHAR8 Year[2]; + CHAR8 Month[2]; + CHAR8 Day[2]; + CHAR8 Hour[2]; + CHAR8 Minute[2]; + CHAR8 Seconds[2]; +} ASN1_UTC_TIME; + +typedef struct { + CHAR8 Year[4]; + CHAR8 Month[2]; + CHAR8 Day[2]; + CHAR8 Hour[2]; + CHAR8 Minute[2]; + CHAR8 Seconds[2]; +} ASN1_GENERALIZED_TIME; + +typedef struct { + CHAR8 Sign; + CHAR8 Hour[2]; + CHAR8 Minute[2]; +} ASN1_TIMEZONE; + #pragma pack() -// The Sovereign Boot Wizard must be linked with Mbed TLS BaseCryptLib! -// The output of X509GetValidity can have different format depending on -// the library provider! +#define ASN1_TYPE_UTC_TIME 0x17 +#define ASN1_TYPE_GENERALIZED_TIME 0x18 + +typedef union { + ASN1_UTC_TIME *UtcTime; + ASN1_GENERALIZED_TIME *GeneralizedTime; +} ASN1_TIME_PTR_UNION; + +#define ASN1_FLAG_MSTRING 0x40 typedef struct { - INT32 Year; - INT32 Month; - INT32 Day; - INT32 Hour; - INT32 Minute; - INT32 Second; -} MBED_TLS_DATETIME_OBECT; + INT32 Length; + INT32 Type; + ASN1_TIME_PTR_UNION Data; + /* + * The value of the following field depends on the type being held. It + * is mostly being used for BIT_STRING so if the input data has a + * non-zero 'unused bits' value, it will be handled correctly + */ + UINT64 Flags; +} OPENSSL_ASN1_TIME; typedef struct { UINTN Signature; @@ -170,9 +204,9 @@ typedef struct { BOOLEAN CertIsInDbx; BOOLEAN CertIsInDb; BOOLEAN SignatureValid; + BOOLEAN CertIsValid; BOOLEAN CertIsMicrosoft; - // Placed exactly at offset 8 for alignment UINT8 CertDigest[SHA256_DIGEST_SIZE]; UINTN CertDigestSize; @@ -185,7 +219,6 @@ typedef struct { } SV_CERT_ENTRY; typedef struct { - // Place first for 8 byte alignment UINT8 ImageDigest[SHA256_DIGEST_SIZE]; UINTN ImageDigestSize; @@ -279,4 +312,27 @@ FinalizeSvBootProvisioning ( VOID ); +BOOLEAN +Asn1TimeToEfiTime ( + IN OPENSSL_ASN1_TIME *Asn1Time, + IN OUT EFI_TIME *EfiTime + ); + +OPENSSL_ASN1_TIME * +EfiTimeToAsn1Time ( + IN EFI_TIME *EfiTime + ); + +VOID +FormatAsn1Time ( + IN OPENSSL_ASN1_TIME *Time, + IN OUT CHAR16 *DateBuffer, + IN UINTN DateBufferSize + ); + +EFI_STATUS +GetCurrentTime ( + IN EFI_TIME *Time + ); + #endif diff --git a/DasharoModulePkg/Application/SovereignBootWizard/SovereignBootWizard.inf b/DasharoModulePkg/Application/SovereignBootWizard/SovereignBootWizard.inf index e111f6aa42..56f4589dc3 100644 --- a/DasharoModulePkg/Application/SovereignBootWizard/SovereignBootWizard.inf +++ b/DasharoModulePkg/Application/SovereignBootWizard/SovereignBootWizard.inf @@ -26,6 +26,7 @@ # [Sources] + Asn1Time.c BootOptionParsing.c KeyManagement.c SignatureParsing.c diff --git a/DasharoModulePkg/Application/SovereignBootWizard/SovereignBootWizardHii.h b/DasharoModulePkg/Application/SovereignBootWizard/SovereignBootWizardHii.h index 315b7f0ff3..be5f426788 100644 --- a/DasharoModulePkg/Application/SovereignBootWizard/SovereignBootWizardHii.h +++ b/DasharoModulePkg/Application/SovereignBootWizard/SovereignBootWizardHii.h @@ -46,6 +46,7 @@ Revision History: #define TRUST_KEY_AND_BOOT_FORM2_QUESTION_ID 0x1202 #define TRUST_KEY_FORM2_QUESTION_ID 0x1203 #define SHOW_KEY_DETAILS_FORM2_QUESTION_ID 0x1204 +#define SKIP_KEY_FORM2_QUESTION_ID 0x1205 #define EXIT_FORM_QUESTION_ID_BASE 0x1F00 #define EXIT_FORM1_QUESTION_ID 0x1F01 diff --git a/DasharoModulePkg/Application/SovereignBootWizard/SovereignBootWizardVfr.vfr b/DasharoModulePkg/Application/SovereignBootWizard/SovereignBootWizardVfr.vfr index d6e74b1c57..6a1bba1b7f 100644 --- a/DasharoModulePkg/Application/SovereignBootWizard/SovereignBootWizardVfr.vfr +++ b/DasharoModulePkg/Application/SovereignBootWizard/SovereignBootWizardVfr.vfr @@ -133,6 +133,12 @@ formset text = STRING_TOKEN(STR_TRUST_KEY), flags = INTERACTIVE, key = TRUST_KEY_FORM2_QUESTION_ID; + + text + help = STRING_TOKEN(STR_EMPTY_STRING), + text = STRING_TOKEN(STR_SKIP_KEY), + flags = INTERACTIVE, + key = SKIP_KEY_FORM2_QUESTION_ID; endif; subtitle text = STRING_TOKEN(STR_EMPTY_STRING); diff --git a/DasharoModulePkg/Application/SovereignBootWizard/SovereignBootWizardVfrStrings.uni b/DasharoModulePkg/Application/SovereignBootWizard/SovereignBootWizardVfrStrings.uni index ad7f924685..15d3546355 100644 --- a/DasharoModulePkg/Application/SovereignBootWizard/SovereignBootWizardVfrStrings.uni +++ b/DasharoModulePkg/Application/SovereignBootWizard/SovereignBootWizardVfrStrings.uni @@ -69,10 +69,11 @@ #string STR_SV_TRUST_KEY_QUESTION #language en-US "Are you sure you want to trust the following" #string STR_SV_UNTRUST_KEY_QUESTION #language en-US "Are you sure you do NOT want to trust the following" -#string STR_DO_NOT_TRUST_KEY #language en-US "[ Do NOT trust, next key/bootloader ]" +#string STR_DO_NOT_TRUST_KEY #language en-US "[ Do NOT trust, next bootloader ]" #string STR_DO_NOT_TRUST_KEY2 #language en-US "[ Do NOT trust ]" #string STR_TRUST_KEY_AND_BOOT #language en-US "[ Trust this key/image and boot ]" #string STR_TRUST_KEY #language en-US "[ Trust this key/image, next key/bootloader ]" +#string STR_SKIP_KEY #language en-US "[ Skip this key/image, next key/bootloader ]" #string STR_SHOW_KEY_DETAILS #language en-US "Show key/certificate details" // Configuration page strings From a8406673c81a8034c7f14a40b0ab6fca76a59a09 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20=C5=BBygowski?= Date: Tue, 29 Jul 2025 15:31:54 +0200 Subject: [PATCH 18/28] UefiBootManagerLib,SovereignBootWizard: Avoid re-entering wizard if not exited MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Michał Żygowski --- .../SovereignBootWizard/SovereignBootWizard.c | 8 ++++ DasharoModulePkg/Include/Guid/SovereignBoot.h | 2 +- .../Library/UefiBootManagerLib/BmBoot.c | 37 ++++++++++++++++++- 3 files changed, 45 insertions(+), 2 deletions(-) diff --git a/DasharoModulePkg/Application/SovereignBootWizard/SovereignBootWizard.c b/DasharoModulePkg/Application/SovereignBootWizard/SovereignBootWizard.c index b4a55aeb37..2050742ca8 100644 --- a/DasharoModulePkg/Application/SovereignBootWizard/SovereignBootWizard.c +++ b/DasharoModulePkg/Application/SovereignBootWizard/SovereignBootWizard.c @@ -916,6 +916,7 @@ SovereignBootWizardInit ( Status = gRT->GetVariable (mSvBootDataVarName, &gSovereignBootWizardFormSetGuid, NULL, &BufferSize, ConfigData); if (EFI_ERROR (Status)) { // Ensure the variable is set if there was an error reading it. + ConfigData->AlreadyStarted = TRUE; gRT->SetVariable ( mSvBootDataVarName, &gSovereignBootWizardFormSetGuid, @@ -937,6 +938,13 @@ SovereignBootWizardInit ( { ConfigData->AppLaunchCause = SV_BOOT_LAUNCH_BOOT_WITH_DEFAULT_SETTINGS; } + ConfigData->AlreadyStarted = TRUE; + gRT->SetVariable ( + mSvBootDataVarName, + &gSovereignBootWizardFormSetGuid, + EFI_VARIABLE_BOOTSERVICE_ACCESS, + BufferSize, + ConfigData); } // If the variable was not set properly, the wizard was probably launched by diff --git a/DasharoModulePkg/Include/Guid/SovereignBoot.h b/DasharoModulePkg/Include/Guid/SovereignBoot.h index eafff0030a..f46091c5cf 100644 --- a/DasharoModulePkg/Include/Guid/SovereignBoot.h +++ b/DasharoModulePkg/Include/Guid/SovereignBoot.h @@ -35,7 +35,7 @@ extern EFI_GUID gSovereignBootWizardFormSetGuid; // Data passed from firmware via EFI variables (volatile, BS access) typedef struct { UINT8 AppLaunchCause; - UINT8 Pad; + BOOLEAN AlreadyStarted; UINT16 BootCurrent; } SOVEREIGN_BOOT_WIZARD_CONFIG_DATA; diff --git a/MdeModulePkg/Library/UefiBootManagerLib/BmBoot.c b/MdeModulePkg/Library/UefiBootManagerLib/BmBoot.c index 70a68022a7..52e44c849c 100644 --- a/MdeModulePkg/Library/UefiBootManagerLib/BmBoot.c +++ b/MdeModulePkg/Library/UefiBootManagerLib/BmBoot.c @@ -1864,6 +1864,7 @@ EfiBootManagerLaunchSovereignBootWizard ( SOVEREIGN_BOOT_WIZARD_CONFIG_DATA SvBootData; UINT16 *BootCurrent; UINTN BootCurrentSize; + UINTN DataSize; BootCurrent = NULL; BootCurrentSize = 0; @@ -1872,6 +1873,23 @@ EfiBootManagerLaunchSovereignBootWizard ( return EFI_NOT_FOUND; } + DataSize = sizeof (SOVEREIGN_BOOT_WIZARD_CONFIG_DATA); + Status = gRT->GetVariable ( + SV_BOOT_DATA_VAR, + &gSovereignBootWizardFormSetGuid, + NULL, + &DataSize, + &SvBootData + ); + + // If we haven't returned from the wizard form, do not launch it again. + // Let the Boot manager return to the wizard instead. + if (!EFI_ERROR (Status) && (DataSize == sizeof (SOVEREIGN_BOOT_WIZARD_CONFIG_DATA))) { + if (SvBootData.AlreadyStarted) { + return EFI_ALREADY_STARTED; + } + } + FilePath = FvFilePath (&gSovereignBootWizardFormSetGuid); if (FilePath == NULL) { return EFI_NOT_FOUND; @@ -1915,6 +1933,16 @@ EfiBootManagerLaunchSovereignBootWizard ( ); EfiBootManagerBoot (&BootOption); + // Clear the launched state as soon as we return from the wizard + SetMem (&SvBootData, sizeof (SvBootData), 0); + gRT->SetVariable ( + SV_BOOT_DATA_VAR, + &gSovereignBootWizardFormSetGuid, + EFI_VARIABLE_BOOTSERVICE_ACCESS, + sizeof (SOVEREIGN_BOOT_WIZARD_CONFIG_DATA), + &SvBootData + ); + // // Remove the boot option after we return from the wizard // @@ -2153,8 +2181,15 @@ EfiBootManagerBoot ( // Only if Sovereign Boot is provisioned. We should not end up in this path before provisioning if ((SvBootConfig != NULL) && SvBootConfig->SvBootEnabled && SvBootConfig->SvBootProvisioned) { + if (SvBootConfig != NULL) { + FreePool (SvBootConfig); + SvBootConfig = NULL; + } + Status = EfiBootManagerLaunchSovereignBootWizard (SV_BOOT_LAUNCH_IMAGE_VERIFICATION_FAILED); - if (EFI_ERROR (Status)) { + // We may have already started the Wizard before and not exited it + // (e.g. by booting a bootloader) + if (EFI_ERROR (Status) && Status != EFI_ALREADY_STARTED) { if (gST->ConOut != NULL) { gST->ConOut->ClearScreen (gST->ConOut); ScreenCleared = TRUE; From 0490ac73c4f662d3942e3198b141034f0759b7b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20=C5=BBygowski?= Date: Tue, 29 Jul 2025 15:35:01 +0200 Subject: [PATCH 19/28] OvmfPkg/DasharoPayloadPkg: Use OpenSSL CryptoLib for image verification MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Michał Żygowski --- DasharoPayloadPkg/DasharoPayloadPkg.dsc | 7 ++++++- OvmfPkg/OvmfPkgX64.dsc | 7 ++++++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/DasharoPayloadPkg/DasharoPayloadPkg.dsc b/DasharoPayloadPkg/DasharoPayloadPkg.dsc index 7471985d75..310c174811 100644 --- a/DasharoPayloadPkg/DasharoPayloadPkg.dsc +++ b/DasharoPayloadPkg/DasharoPayloadPkg.dsc @@ -772,6 +772,9 @@ OrderedCollectionLib|MdePkg/Library/BaseOrderedCollectionRedBlackTreeLib/BaseOrd MdeModulePkg/Universal/SecurityStubDxe/SecurityStubDxe.inf { !if $(SECURE_BOOT_ENABLE) == TRUE + # For Secure Boot use OpenSSL, because MBED TLS may fail AuthenticodeVerify + BaseCryptLib|CryptoPkg/Library/BaseCryptLib/BaseCryptLib.inf + OpensslLib|CryptoPkg/Library/OpensslLib/OpensslLibCrypto.inf NULL|SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationHandler.inf !endif !if $(TPM_ENABLE) == TRUE @@ -787,7 +790,9 @@ OrderedCollectionLib|MdePkg/Library/BaseOrderedCollectionRedBlackTreeLib/BaseOrd !if $(SOVEREIGN_BOOT_ENABLE) == TRUE DasharoModulePkg/Application/SovereignBootWizard/SovereignBootWizard.inf { - BaseCryptLib|CryptoPkg/Library/BaseCryptLibMbedTls/BaseCryptLib.inf + # For Secure Boot use OpenSSL, because MBED TLS may fail AuthenticodeVerify + BaseCryptLib|CryptoPkg/Library/BaseCryptLib/BaseCryptLib.inf + OpensslLib|CryptoPkg/Library/OpensslLib/OpensslLibCrypto.inf } !endif !endif diff --git a/OvmfPkg/OvmfPkgX64.dsc b/OvmfPkg/OvmfPkgX64.dsc index 00fce76b64..ceb02b2000 100644 --- a/OvmfPkg/OvmfPkgX64.dsc +++ b/OvmfPkg/OvmfPkgX64.dsc @@ -883,6 +883,9 @@ MdeModulePkg/Universal/SecurityStubDxe/SecurityStubDxe.inf { !if $(SECURE_BOOT_ENABLE) == TRUE + # For Secure Boot use OpenSSL, because MBED TLS may fail AuthenticodeVerify + BaseCryptLib|CryptoPkg/Library/BaseCryptLib/BaseCryptLib.inf + OpensslLib|CryptoPkg/Library/OpensslLib/OpensslLibCrypto.inf NULL|SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationHandler.inf !endif !include OvmfPkg/Include/Dsc/OvmfTpmSecurityStub.dsc.inc @@ -1090,7 +1093,9 @@ !if $(SOVEREIGN_BOOT_ENABLE) == TRUE DasharoModulePkg/Application/SovereignBootWizard/SovereignBootWizard.inf { - BaseCryptLib|CryptoPkg/Library/BaseCryptLibMbedTls/BaseCryptLib.inf + # For Secure Boot use OpenSSL, because MBED TLS may fail AuthenticodeVerify + BaseCryptLib|CryptoPkg/Library/BaseCryptLib/BaseCryptLib.inf + OpensslLib|CryptoPkg/Library/OpensslLib/OpensslLibCrypto.inf } !endif From 685e72a47011f09d072050d6d9ae5dbe3b7dd306 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20=C5=BBygowski?= Date: Wed, 30 Jul 2025 10:34:25 +0200 Subject: [PATCH 20/28] DMP/Application/SovereignBootWizard: Show pop-ups when cert is invalid MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Michał Żygowski --- .../SovereignBootWizard/KeyManagement.c | 38 +++++++++++++++++++ .../SovereignBootWizard/SignatureParsing.c | 6 --- 2 files changed, 38 insertions(+), 6 deletions(-) diff --git a/DasharoModulePkg/Application/SovereignBootWizard/KeyManagement.c b/DasharoModulePkg/Application/SovereignBootWizard/KeyManagement.c index 1854deab77..06a67a4867 100644 --- a/DasharoModulePkg/Application/SovereignBootWizard/KeyManagement.c +++ b/DasharoModulePkg/Application/SovereignBootWizard/KeyManagement.c @@ -217,6 +217,7 @@ EnrollHashToSigDB ( SV_CERT_ENTRY *CertificateEntry; EFI_STATUS Status; BOOLEAN Trust; + EFI_INPUT_KEY Key; BootloaderEntry = GetMenuEntry (&BootOptionMenu, mBootloaderIndex); if (BootloaderEntry == NULL) { @@ -316,6 +317,43 @@ EnrollHashToSigDB ( goto ON_EXIT; } + if (SecurityContext->ImageIsSigned && Trust) { + if (!CertificateEntry->CertIsValid) { + do { + CreatePopUp ( + EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE, + &Key, + L"", + L"The certificate is not yet valid or expired.", + L"Can not add the certificate as trusted." + L"", + L"Press ENTER to abort the process...", + L"", + NULL + ); + } while (Key.UnicodeChar != CHAR_CARRIAGE_RETURN); + Status = EFI_ABORTED; + goto ON_EXIT; + } + if (!CertificateEntry->SignatureValid) { + do { + CreatePopUp ( + EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE, + &Key, + L"", + L"The image signature verification failed with this certificate.", + L"Can not add the certificate as trusted." + L"", + L"Press ENTER to abort the process...", + L"", + NULL + ); + } while (Key.UnicodeChar != CHAR_CARRIAGE_RETURN); + Status = EFI_ABORTED; + goto ON_EXIT; + } + } + Status = SetSecureBootMode (CUSTOM_SECURE_BOOT_MODE); if (EFI_ERROR (Status)) { DEBUG ((DEBUG_ERROR, "Failed to set custom Secure Boot mode: %r\n", Status)); diff --git a/DasharoModulePkg/Application/SovereignBootWizard/SignatureParsing.c b/DasharoModulePkg/Application/SovereignBootWizard/SignatureParsing.c index 56404467c9..ffc3529697 100644 --- a/DasharoModulePkg/Application/SovereignBootWizard/SignatureParsing.c +++ b/DasharoModulePkg/Application/SovereignBootWizard/SignatureParsing.c @@ -834,12 +834,6 @@ UpdateCertInfo ( mCertIndex++; continue; } - if (!CertificateEntry->CertIsValid || !CertificateEntry->SignatureValid) { - DEBUG ((DEBUG_INFO, "Certificate %u or itssignature is invalid\n", - mCertIndex)); - mCertIndex++; - continue; - } } break; From d98a8af4c7ddddb30542666be176e0c427b2ee69 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20=C5=BBygowski?= Date: Wed, 30 Jul 2025 10:35:13 +0200 Subject: [PATCH 21/28] DMP/Application/SovereignBootWizard: Free all buffers on unload MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Michał Żygowski --- .../SovereignBootWizard/BootOptionParsing.c | 80 +++++++++++++++++-- .../SovereignBootWizard/KeyManagement.c | 12 +-- .../SovereignBootWizard/SignatureParsing.c | 79 ++++++++++++------ .../SovereignBootWizard/SovereignBootWizard.c | 16 ++-- .../SovereignBootWizard/SovereignBootWizard.h | 35 ++++---- 5 files changed, 159 insertions(+), 63 deletions(-) diff --git a/DasharoModulePkg/Application/SovereignBootWizard/BootOptionParsing.c b/DasharoModulePkg/Application/SovereignBootWizard/BootOptionParsing.c index 3940943458..b9f273c0e5 100644 --- a/DasharoModulePkg/Application/SovereignBootWizard/BootOptionParsing.c +++ b/DasharoModulePkg/Application/SovereignBootWizard/BootOptionParsing.c @@ -17,6 +17,11 @@ SV_MENU_OPTION BootOptionMenu = { 0 }; +STATIC VOID +FreeBootMenuEntry ( + SV_MENU_ENTRY *BootloaderEntry + ); + /** This function converts an input device structure to a Unicode string. @@ -98,7 +103,7 @@ StripFilePath ( } // In case we did not found file path, free the duplicated path - FreePool (Path); + FREE_NON_NULL (Path); return NULL; } @@ -368,20 +373,21 @@ GetBootOptions ( PathString = UiDevicePathToStr (Private->DevPathToText, NewLoadContext->FilePath); } else { PathString = UiDevicePathToStr (Private->DevPathToText, HwDevicePath); - FreePool (HwDevicePath); + FREE_NON_NULL (HwDevicePath); } ASSERT (PathString != NULL); StringSize = StrSize (L"Hardware path: ") + StrSize (PathString) + sizeof(CHAR16); NewMenuEntry->DevicePathString = AllocateZeroPool (StringSize); ASSERT (NewMenuEntry->DevicePathString != NULL); UnicodeSPrint (NewMenuEntry->DevicePathString, StringSize, L"Hardware path: %s", PathString); - FreePool (PathString); + FREE_NON_NULL (PathString); NewMenuEntry->DevicePathStringToken = HiiSetString (Private->HiiHandle, 0, NewMenuEntry->DevicePathString, NULL); // File path on the disk if (HwDevicePath != NULL) { PathString = UiDevicePathToStr (Private->DevPathToText, ExtractFilePath (NewLoadContext->FilePath)); if (PathString == NULL) { + FreePool (HwDevicePath); return EFI_OUT_OF_RESOURCES; } } else { @@ -394,12 +400,16 @@ GetBootOptions ( StringSize = StrSize (L"File path: ") + StrSize (PathString) + sizeof(CHAR16); NewMenuEntry->FilePathString = AllocateZeroPool (StringSize); if (NewMenuEntry->FilePathString == NULL) { + if (HwDevicePath != NULL) { + FREE_NON_NULL (PathString); + FreePool (HwDevicePath); + } return EFI_OUT_OF_RESOURCES; } UnicodeSPrint (NewMenuEntry->FilePathString, StringSize, L"File path: %s", PathString); if (HwDevicePath != NULL) { - FreePool (PathString); + FREE_NON_NULL (PathString); } NewMenuEntry->FilePathStringToken = HiiSetString (Private->HiiHandle, 0, NewMenuEntry->FilePathString, NULL); @@ -410,9 +420,7 @@ GetBootOptions ( EfiBootManagerFreeLoadOptions (BootOption, BootOptionCount); - if (BootOrderList != NULL) { - FreePool (BootOrderList); - } + FREE_NON_NULL (BootOrderList); DEBUG ((DEBUG_INFO, "Found %d boot options \n", MenuCount)); @@ -524,3 +532,61 @@ UpdateBootloaderPage ( return Status; } + +STATIC VOID +FreeLoadContext ( + SV_LOAD_CONTEXT *LoadCtx + ) +{ + if (LoadCtx == NULL) { + return; + } + + FREE_NON_NULL (LoadCtx->Description); + FREE_NON_NULL (LoadCtx->FilePath); + FREE_NON_NULL (LoadCtx->OptionalData); +} + +STATIC VOID +FreeBootMenuEntry ( + SV_MENU_ENTRY *BootloaderEntry + ) +{ + if (BootloaderEntry == NULL) { + return; + } + + FreeLoadContext (BootloaderEntry->VariableContext); + FreeSecurityContext (BootloaderEntry->SecurityContext); + + FREE_NON_NULL (BootloaderEntry->SecurityContext); + FREE_NON_NULL (BootloaderEntry->VariableContext); + FREE_NON_NULL (BootloaderEntry->DisplayString); + FREE_NON_NULL (BootloaderEntry->DevicePathString); + FREE_NON_NULL (BootloaderEntry->FilePathString); +} + + +VOID +FreeBootMenuEntries ( + VOID + ) +{ + SV_MENU_ENTRY *BootloaderEntry; + + if (BootOptionMenu.MenuNumber == 0) { + return; + } + + while (!IsListEmpty (&BootOptionMenu.Head)) { + BootloaderEntry = CR ( + BootOptionMenu.Head.ForwardLink, + SV_MENU_ENTRY, + Link, + SOVEREIGN_BOOT_MENU_ENTRY_SIGNATURE); + RemoveEntryList (&BootloaderEntry->Link); + FreeBootMenuEntry (BootloaderEntry); + } + + BootOptionMenu.MenuNumber = 0; +} diff --git a/DasharoModulePkg/Application/SovereignBootWizard/KeyManagement.c b/DasharoModulePkg/Application/SovereignBootWizard/KeyManagement.c index 06a67a4867..8fa956a384 100644 --- a/DasharoModulePkg/Application/SovereignBootWizard/KeyManagement.c +++ b/DasharoModulePkg/Application/SovereignBootWizard/KeyManagement.c @@ -151,7 +151,7 @@ PrepareSbVariablesForSvBoot ( @retval EFI_SUCCESS Signature List was created successfully. @retval EFI_OUT_OF_RESOURCES Failed to allocate memory. **/ -EFI_STATUS +STATIC EFI_STATUS CreateSigList ( IN VOID *Data, IN UINTN Size, @@ -391,9 +391,7 @@ EnrollHashToSigDB ( ON_EXIT: - if (SigDBHash != NULL) { - FreePool (SigDBHash); - } + FREE_NON_NULL (SigDBHash); return Status; } @@ -578,9 +576,7 @@ EnrollEphemeralPk ( ON_EXIT: - if (SigList != NULL) { - FreePool (SigList); - } + FREE_NON_NULL (SigList); if (RsaCtx != NULL) { RsaFree (RsaCtx); @@ -589,7 +585,7 @@ EnrollEphemeralPk ( return Status; } -BOOLEAN +STATIC BOOLEAN IsDbEmpty ( VOID ) diff --git a/DasharoModulePkg/Application/SovereignBootWizard/SignatureParsing.c b/DasharoModulePkg/Application/SovereignBootWizard/SignatureParsing.c index ffc3529697..4f49a87354 100644 --- a/DasharoModulePkg/Application/SovereignBootWizard/SignatureParsing.c +++ b/DasharoModulePkg/Application/SovereignBootWizard/SignatureParsing.c @@ -156,9 +156,7 @@ CertIsValid ( ON_EXIT: - if (CurrentTime != NULL) { - FreePool (CurrentTime); - } + FREE_NON_NULL (CurrentTime); return Valid; } @@ -415,9 +413,7 @@ FillCertificateEntries ( InsertTailList (&SecCtx->Certs, &NewCertEntry->CertLink); CertCount++; } else { - if (NewCertEntry != NULL) { - FreePool (NewCertEntry); - } + FREE_NON_NULL (NewCertEntry); } CertPtr += *(UINT32 *)CertPtr; // Certificate size @@ -458,10 +454,8 @@ FillCertificateEntries ( InsertTailList (&SecCtx->Certs, &NewCertEntry->CertLink); CertCount++; } else { - DEBUG ((DEBUG_ERROR, "Failed to add new certificate: %r\n", CertStatus)); - if (NewCertEntry != NULL) { - FreePool (NewCertEntry); - } + DEBUG ((DEBUG_ERROR, "Failed to add new certificate: %r\n", CertStatus)); + FREE_NON_NULL (NewCertEntry); } CertPtr += *(UINT32 *)CertPtr; // Certificate size @@ -531,8 +525,8 @@ FillSecurityContext ( &AuthStatus ); - if (LoadCtx->NeedsPathExpansion && (FullFilePath != NULL)) { - FreePool (FullFilePath); + if (LoadCtx->NeedsPathExpansion) { + FREE_NON_NULL (FullFilePath); } if (EFI_ERROR (Status)) { @@ -593,9 +587,7 @@ FillSecurityContext ( SecCtx->AuthenticationStatus, SecCtx->NumCertificates)); - if (ImageBase != NULL) { - FreePool (ImageBase); - } + FREE_NON_NULL (ImageBase); return Status; } @@ -799,7 +791,7 @@ UpdateCertInfo ( Status = ParseHashValue (SecurityContext->ImageDigest, SecurityContext->ImageDigestSize, &NewString); if (!EFI_ERROR (Status)) { HiiSetString (Private->HiiHandle, STRING_TOKEN (STR_KEY_FINGERPRINT_HASH), NewString, NULL); - FreePool (NewString); + FREE_NON_NULL (NewString); } else { HiiSetString (Private->HiiHandle, STRING_TOKEN (STR_KEY_FINGERPRINT_HASH), L"Image hash could not be obtained.", NULL); } @@ -850,7 +842,7 @@ UpdateCertInfo ( Status = ParseHashValue (CertificateEntry->CertDigest, CertificateEntry->CertDigestSize, &NewString); if (!EFI_ERROR (Status)) { HiiSetString (Private->HiiHandle, STRING_TOKEN (STR_KEY_FINGERPRINT_HASH), NewString, NULL); - FreePool (NewString); + FREE_NON_NULL (NewString); } else { HiiSetString (Private->HiiHandle, STRING_TOKEN (STR_KEY_FINGERPRINT_HASH), L"Could not obtain certificate fingerprint", NULL); } @@ -970,7 +962,7 @@ UpdateCertSerialNumberString ( // part of the serial number itself, thus we should parse one byte less. if (!EFI_ERROR (ParseHashValue (StringBuffer, StringBufferSize - 1, &NewString))) { HiiSetString (Private->HiiHandle, STRING_TOKEN (STR_CERT_SERIAL_NUMBER2), NewString, NULL); - FreePool (NewString); + FREE_NON_NULL (NewString); } } } @@ -1011,15 +1003,13 @@ UpdateCertKeyStrings ( if (PubKeyExpSize > 4) { DEBUG ((DEBUG_ERROR, "Key exponent size too big\n")); - RsaFree (X509PubKey); - return; + goto ON_EXIT; } ModulusBuffer = (UINT8 *) AllocateZeroPool (PubKeyModSize); if (ModulusBuffer == NULL) { DEBUG ((DEBUG_ERROR, "Could not allocate memory for modulus\n")); - RsaFree (X509PubKey); - return; + goto ON_EXIT; } if (!RsaGetKey (X509PubKey, RsaKeyN, ModulusBuffer, &PubKeyModSize)) { @@ -1032,10 +1022,11 @@ UpdateCertKeyStrings ( goto ON_EXIT; } + NewString = NULL; if (!EFI_ERROR (ParseKeyModulus (ModulusBuffer, PubKeyModSize, &NewString))) { HiiSetString (Private->HiiHandle, STRING_TOKEN (STR_CERT_KEY_MODULUS_HEX), NewString, NULL); - FreePool (NewString); } + FREE_NON_NULL (NewString); SetMem(ExponentString, sizeof (ExponentString), 0); UnicodeSPrint(ExponentString, sizeof (ExponentString), L"0x%X", Exponent); @@ -1044,7 +1035,7 @@ UpdateCertKeyStrings ( ON_EXIT: RsaFree (X509PubKey); - FreePool (ModulusBuffer); + FREE_NON_NULL (ModulusBuffer); } EFI_STATUS @@ -1072,3 +1063,43 @@ UpdateCertDetails ( return EFI_SUCCESS; } + +STATIC VOID +FreeCertificateEntry ( + SV_CERT_ENTRY *CertEntry + ) +{ + if (CertEntry == NULL) { + return; + } + + FREE_NON_NULL (CertEntry->CertData); +} + +VOID +FreeSecurityContext ( + SV_SECURITY_CONTEXT *SecCtx + ) +{ + SV_CERT_ENTRY *CertEntry; + + if (SecCtx == NULL) { + return; + } + + if (SecCtx->NumCertificates == 0) { + return; + } + + while (!IsListEmpty (&SecCtx->Certs)) { + CertEntry = CR ( + SecCtx->Certs.ForwardLink, + SV_CERT_ENTRY, + CertLink, + SOVEREIGN_BOOT_CERT_ENTRY_SIGNATURE); + RemoveEntryList (&CertEntry->CertLink); + FreeCertificateEntry (CertEntry); + } + + SecCtx->NumCertificates = 0; +} diff --git a/DasharoModulePkg/Application/SovereignBootWizard/SovereignBootWizard.c b/DasharoModulePkg/Application/SovereignBootWizard/SovereignBootWizard.c index 2050742ca8..45581bbc44 100644 --- a/DasharoModulePkg/Application/SovereignBootWizard/SovereignBootWizard.c +++ b/DasharoModulePkg/Application/SovereignBootWizard/SovereignBootWizard.c @@ -121,7 +121,7 @@ ExtractConfig ( ConfigRequestHdr, (UINT64) BufferSize ); - FreePool (ConfigRequestHdr); + FREE_NON_NULL (ConfigRequestHdr); } // Convert fields of binary structure to string representation. @@ -1037,13 +1037,13 @@ SovereignBootWizardInit ( NewString = HiiGetString (HiiHandle, STRING_TOKEN (FUNCTION_TEN_STRING), NULL); ASSERT (NewString != NULL); mPrivateData->FormBrowserEx2->RegisterHotKey (&HotKey, BROWSER_ACTION_SUBMIT, 0, NewString); - FreePool (NewString); + FREE_NON_NULL (NewString); HotKey.ScanCode = SCAN_F9; NewString = HiiGetString (HiiHandle, STRING_TOKEN (FUNCTION_NINE_STRING), NULL); ASSERT (NewString != NULL); mPrivateData->FormBrowserEx2->RegisterHotKey (&HotKey, BROWSER_ACTION_DEFAULT, EFI_HII_DEFAULT_CLASS_STANDARD, NewString); - FreePool (NewString); + FREE_NON_NULL (NewString); } Status = SovereignBootWizardUnload (ImageHandle); @@ -1085,14 +1085,12 @@ SovereignBootWizardUnload ( } for (Index = 0; Index < NAME_VALUE_NAME_NUMBER; Index++) { - if (mPrivateData->NameValueName[Index] != NULL) { - FreePool (mPrivateData->NameValueName[Index]); - } + FREE_NON_NULL (mPrivateData->NameValueName[Index]); } - // TODO: Free all pools from certificate, bootloader contexts and entries - - FreePool (mPrivateData); + // Free all pools from certificate, bootloader contexts and entries + FreeBootMenuEntries (); + FREE_NON_NULL (mPrivateData); mPrivateData = NULL; return EFI_SUCCESS; diff --git a/DasharoModulePkg/Application/SovereignBootWizard/SovereignBootWizard.h b/DasharoModulePkg/Application/SovereignBootWizard/SovereignBootWizard.h index 4138a0cff5..98b92bef48 100644 --- a/DasharoModulePkg/Application/SovereignBootWizard/SovereignBootWizard.h +++ b/DasharoModulePkg/Application/SovereignBootWizard/SovereignBootWizard.h @@ -79,6 +79,14 @@ extern UINT8 SovereignBootWizardStrings[]; #define SOVEREIGN_BOOT_LOAD_CONTEXT_SELECT 0x0 #define SOVEREIGN_BOOT_FILE_CONTEXT_SELECT 0x2 +#define FREE_NON_NULL(Pointer) \ + do { \ + if ((Pointer) != NULL) { \ + FreePool((Pointer)); \ + (Pointer) = NULL; \ + } \ + } while(FALSE) + typedef struct { UINTN Signature; @@ -207,7 +215,7 @@ typedef struct { BOOLEAN CertIsValid; BOOLEAN CertIsMicrosoft; - UINT8 CertDigest[SHA256_DIGEST_SIZE]; + UINT8 CertDigest[MAX_DIGEST_SIZE]; UINTN CertDigestSize; UINTN CertDataSize; @@ -219,7 +227,7 @@ typedef struct { } SV_CERT_ENTRY; typedef struct { - UINT8 ImageDigest[SHA256_DIGEST_SIZE]; + UINT8 ImageDigest[MAX_DIGEST_SIZE]; UINTN ImageDigestSize; BOOLEAN ImageIsInDbx; @@ -294,19 +302,6 @@ AddKeyOrHashAsTrustedOrUntrusted ( BOOLEAN Trust ); -EFI_STATUS -CreateSigList ( - IN VOID *Data, - IN UINTN Size, - IN EFI_GUID *SigType, - OUT EFI_SIGNATURE_LIST **SigList - ); - -BOOLEAN -IsDbEmpty ( - VOID - ); - EFI_STATUS FinalizeSvBootProvisioning ( VOID @@ -335,4 +330,14 @@ GetCurrentTime ( IN EFI_TIME *Time ); +VOID +FreeBootMenuEntries ( + VOID + ); + +VOID +FreeSecurityContext ( + SV_SECURITY_CONTEXT *SecCtx + ); + #endif From e992ecd421ad0cd972621f19b006734ff880579c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20=C5=BBygowski?= Date: Wed, 30 Jul 2025 11:54:24 +0200 Subject: [PATCH 22/28] DMP/Application/SovereignBootWizard: Refactor bootloader parsing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Michał Żygowski --- .../SovereignBootWizard/BootOptionParsing.c | 411 ++++++++++-------- .../SovereignBootWizard/SovereignBootWizard.c | 6 +- 2 files changed, 236 insertions(+), 181 deletions(-) diff --git a/DasharoModulePkg/Application/SovereignBootWizard/BootOptionParsing.c b/DasharoModulePkg/Application/SovereignBootWizard/BootOptionParsing.c index b9f273c0e5..524b46c445 100644 --- a/DasharoModulePkg/Application/SovereignBootWizard/BootOptionParsing.c +++ b/DasharoModulePkg/Application/SovereignBootWizard/BootOptionParsing.c @@ -178,28 +178,16 @@ CreateMenuEntry ( return MenuEntry; } -/** - - Build the BootOptionMenu according to BootOrder Variable. - This Routine will access the Boot#### to get EFI_LOAD_OPTION. - - @param CallbackData The BMM context data. - - @return EFI_NOT_FOUND Fail to find "BootOrder" variable. - @return EFI_SUCESS Success build boot option menu. - -**/ -EFI_STATUS -GetBootOptions ( - IN SOVEREIGN_BOOT_WIZARD_PRIVATE_DATA *Private +STATIC EFI_STATUS +FillMenuEntry ( + IN SOVEREIGN_BOOT_WIZARD_PRIVATE_DATA *Private, + IN UINT16 BootOptionIndex, + IN OUT SV_MENU_ENTRY **MenuEntry ) { - UINTN Index; CHAR16 BootString[10]; UINT8 *LoadOptionFromVar; UINTN BootOptionSize; - UINT16 *BootOrderList; - UINTN BootOrderListSize; UINT8 *LoadOptionPtr; UINT8 *LoadOptionEnd; SV_MENU_ENTRY *NewMenuEntry; @@ -209,213 +197,280 @@ GetBootOptions ( UINTN DescriptionSize; EFI_DEVICE_PATH_PROTOCOL *DevicePath; EFI_DEVICE_PATH_PROTOCOL *HwDevicePath; - UINTN MenuCount; UINT8 *Ptr; - EFI_BOOT_MANAGER_LOAD_OPTION *BootOption; - UINTN BootOptionCount; CHAR16 *PathString; - MenuCount = 0; - BootOrderListSize = 0; - BootOrderList = NULL; - LoadOptionFromVar = NULL; - InitializeListHead (&BootOptionMenu.Head); + if ((Private == NULL) || (MenuEntry == NULL)) { + return EFI_INVALID_PARAMETER; + } - DEBUG ((DEBUG_INFO, "Locating boot options\n")); + UnicodeSPrint (BootString, sizeof (BootString), L"Boot%04x", BootOptionIndex); // - // Get the BootOrder from the Var + // Get all loadoptions from the VAR // - GetEfiGlobalVariable2 (L"BootOrder", (VOID **)&BootOrderList, &BootOrderListSize); - if (BootOrderList == NULL) { - return EFI_NOT_FOUND; + GetEfiGlobalVariable2 (BootString, (VOID **)&LoadOptionFromVar, &BootOptionSize); + if (LoadOptionFromVar == NULL) { + return EFI_OUT_OF_RESOURCES; } - BootOption = EfiBootManagerGetLoadOptions (&BootOptionCount, LoadOptionTypeBoot); - for (Index = 0; Index < BootOrderListSize / sizeof (UINT16); Index++) { - // - // Don't display the hidden/inactive boot option - // - if (((BootOption[Index].Attributes & LOAD_OPTION_HIDDEN) != 0) || - ((BootOption[Index].Attributes & LOAD_OPTION_ACTIVE) == 0)) { - continue; - } + Ptr = (UINT8 *)LoadOptionFromVar; - // TODO: Some boot options may not have entries returned by EfiBootManagerGetLoadOptions - // Query the Boot#### variable directly using BootCurrent index. - if (Private->ConfigData.AppLaunchCause == SV_BOOT_LAUNCH_IMAGE_VERIFICATION_FAILED) { - if (BootOrderList[Index] != Private->ConfigData.BootCurrent) { - continue; - } - } + // + // Attribute = *(UINT32 *)Ptr; + // + Ptr += sizeof (UINT32); - UnicodeSPrint (BootString, sizeof (BootString), L"Boot%04x", BootOrderList[Index]); - // - // Get all loadoptions from the VAR - // - GetEfiGlobalVariable2 (BootString, (VOID **)&LoadOptionFromVar, &BootOptionSize); - if (LoadOptionFromVar == NULL) { - continue; - } + // + // FilePathSize = *(UINT16 *)Ptr; + // + Ptr += sizeof (UINT16); - // - // Is a Legacy Device? - // - Ptr = (UINT8 *)LoadOptionFromVar; + // + // Description = (CHAR16 *)Ptr; + // + DescriptionSize = StrSize ((CHAR16 *)Ptr); + Ptr += DescriptionSize; - // - // Attribute = *(UINT32 *)Ptr; - // - Ptr += sizeof (UINT32); + // + // Now Ptr point to Device Path + // + DevicePath = (EFI_DEVICE_PATH_PROTOCOL *)Ptr; - // - // FilePathSize = *(UINT16 *)Ptr; - // - Ptr += sizeof (UINT16); + // Skip boot options that do not point to disks + if (!IsHddFilePath (DevicePath)) { + FreePool (LoadOptionFromVar); + return EFI_ABORTED; + } - // - // Description = (CHAR16 *)Ptr; - // - DescriptionSize = StrSize ((CHAR16 *)Ptr); - Ptr += DescriptionSize; + NewMenuEntry = CreateMenuEntry (SOVEREIGN_BOOT_LOAD_CONTEXT_SELECT); + if (NewMenuEntry == NULL) { + FreePool (LoadOptionFromVar); + return EFI_OUT_OF_RESOURCES; + } - // - // Now Ptr point to Device Path - // - DevicePath = (EFI_DEVICE_PATH_PROTOCOL *)Ptr; + NewLoadContext = (SV_LOAD_CONTEXT *)NewMenuEntry->VariableContext; - // Skip boot options that do not point to disks - if (!IsHddFilePath (DevicePath)) { - FreePool (LoadOptionFromVar); - continue; - } + LoadOptionPtr = LoadOptionFromVar; + LoadOptionEnd = LoadOptionFromVar + BootOptionSize; - NewMenuEntry = CreateMenuEntry (SOVEREIGN_BOOT_LOAD_CONTEXT_SELECT); - if (NewMenuEntry == NULL) { - return EFI_OUT_OF_RESOURCES; - } + NewMenuEntry->OptionNumber = BootOptionIndex; - NewLoadContext = (SV_LOAD_CONTEXT *)NewMenuEntry->VariableContext; + if ((BBS_DEVICE_PATH == DevicePath->Type) && (BBS_BBS_DP == DevicePath->SubType)) { + NewLoadContext->IsLegacy = TRUE; + } else { + NewLoadContext->IsLegacy = FALSE; + } - LoadOptionPtr = LoadOptionFromVar; - LoadOptionEnd = LoadOptionFromVar + BootOptionSize; + // + // LoadOption is a pointer type of UINT8 + // for easy use with following LOAD_OPTION + // embedded in this struct + // + NewLoadContext->Attributes = *(UINT32 *)LoadOptionPtr; - NewMenuEntry->OptionNumber = BootOrderList[Index]; + LoadOptionPtr += sizeof (UINT32); - if ((BBS_DEVICE_PATH == DevicePath->Type) && (BBS_BBS_DP == DevicePath->SubType)) { - NewLoadContext->IsLegacy = TRUE; - } else { - NewLoadContext->IsLegacy = FALSE; - } + NewLoadContext->FilePathLength = *(UINT16 *)LoadOptionPtr; + LoadOptionPtr += sizeof (UINT16); - // - // LoadOption is a pointer type of UINT8 - // for easy use with following LOAD_OPTION - // embedded in this struct - // - NewLoadContext->Attributes = *(UINT32 *)LoadOptionPtr; + NewLoadContext->Description = AllocateZeroPool (DescriptionSize); + if (NewLoadContext->Description == NULL) { + FreePool (LoadOptionFromVar); + return EFI_OUT_OF_RESOURCES; + } + StrCpyS (NewLoadContext->Description, DescriptionSize / sizeof(CHAR16), (CONST CHAR16 *)LoadOptionPtr); - LoadOptionPtr += sizeof (UINT32); + StringSize = StrSize (L"Description: ") + DescriptionSize; + NewMenuEntry->DisplayString = AllocateZeroPool (StringSize); + if (NewMenuEntry->DisplayString == NULL) { + FreePool (LoadOptionFromVar); + return EFI_OUT_OF_RESOURCES; + } - NewLoadContext->FilePathLength = *(UINT16 *)LoadOptionPtr; - LoadOptionPtr += sizeof (UINT16); + UnicodeSPrint (NewMenuEntry->DisplayString, StringSize, L"Description: %s", LoadOptionPtr); + NewMenuEntry->DisplayStringToken = HiiSetString ( + Private->HiiHandle, + 0, + NewMenuEntry->DisplayString, + NULL); - StringSize = StrSize (L"Description: ") + DescriptionSize + sizeof(CHAR16); - NewLoadContext->Description = AllocateZeroPool (StringSize); - if (NewLoadContext->Description == NULL) { - return EFI_OUT_OF_RESOURCES; - } + LoadOptionPtr += StrSize ((UINT16 *)LoadOptionPtr); + + NewLoadContext->FilePath = AllocateZeroPool (NewLoadContext->FilePathLength); + if (NewLoadContext->FilePath == NULL) { + FreePool (LoadOptionFromVar); + return EFI_OUT_OF_RESOURCES; + } - UnicodeSPrint (NewLoadContext->Description, StringSize, L"Description: %s", LoadOptionPtr); + CopyMem ( + NewLoadContext->FilePath, + (EFI_DEVICE_PATH_PROTOCOL *)LoadOptionPtr, + NewLoadContext->FilePathLength + ); - NewMenuEntry->DisplayString = NewLoadContext->Description; - NewMenuEntry->DisplayStringToken = HiiSetString (Private->HiiHandle, 0, NewLoadContext->Description, NULL); + LoadOptionPtr += NewLoadContext->FilePathLength; - LoadOptionPtr += StrSize ((UINT16 *)LoadOptionPtr); + if (LoadOptionPtr < LoadOptionEnd) { + OptionalDataSize = BootOptionSize - + sizeof (UINT32) - + sizeof (UINT16) - + DescriptionSize - + NewLoadContext->FilePathLength; + NewLoadContext->OptionalData = AllocateZeroPool (OptionalDataSize); - NewLoadContext->FilePath = AllocateZeroPool (NewLoadContext->FilePathLength); - if (NewLoadContext->FilePath == NULL) { + if (NewLoadContext->OptionalData == NULL) { + FreePool (LoadOptionFromVar); return EFI_OUT_OF_RESOURCES; } CopyMem ( - NewLoadContext->FilePath, - (EFI_DEVICE_PATH_PROTOCOL *)LoadOptionPtr, - NewLoadContext->FilePathLength + NewLoadContext->OptionalData, + LoadOptionPtr, + OptionalDataSize ); + NewLoadContext->OptionalDataSize = OptionalDataSize; + } - LoadOptionPtr += NewLoadContext->FilePathLength; + // Hardware Path to the disk + HwDevicePath = StripFilePath (NewLoadContext->FilePath); + if (HwDevicePath == NULL) { + // In case there is no file device path, it means it is /EFI/BOOT/BOOTX64.efi + // and is automatically expanded by UEFI boot manager + PathString = UiDevicePathToStr (Private->DevPathToText, NewLoadContext->FilePath); + NewLoadContext->NeedsPathExpansion = TRUE; + } else { + PathString = UiDevicePathToStr (Private->DevPathToText, HwDevicePath); + FREE_NON_NULL (HwDevicePath); + } + ASSERT (PathString != NULL); + StringSize = StrSize (L"Hardware path: ") + StrSize (PathString) + sizeof(CHAR16); + NewMenuEntry->DevicePathString = AllocateZeroPool (StringSize); + ASSERT (NewMenuEntry->DevicePathString != NULL); + UnicodeSPrint (NewMenuEntry->DevicePathString, StringSize, L"Hardware path: %s", PathString); + FREE_NON_NULL (PathString); + NewMenuEntry->DevicePathStringToken = HiiSetString ( + Private->HiiHandle, + 0, + NewMenuEntry->DevicePathString, + NULL); + + // File path on the disk + if (!NewLoadContext->NeedsPathExpansion) { + PathString = UiDevicePathToStr ( + Private->DevPathToText, + ExtractFilePath (NewLoadContext->FilePath)); + if (PathString == NULL) { + FREE_NON_NULL (LoadOptionFromVar); + return EFI_OUT_OF_RESOURCES; + } + } else { + // In case there is no file device path, it means it is /EFI/BOOT/BOOTX64.efi + // and is automatically expanded by UEFI boot manager. However when reading + // the file in the application, we have to expand it ourselves. + PathString = EFI_REMOVABLE_MEDIA_FILE_NAME; + } + StringSize = StrSize (L"File path: ") + StrSize (PathString) + sizeof(CHAR16); + NewMenuEntry->FilePathString = AllocateZeroPool (StringSize); + if (NewMenuEntry->FilePathString == NULL) { + if (!NewLoadContext->NeedsPathExpansion) { + FREE_NON_NULL (PathString); + } + return EFI_OUT_OF_RESOURCES; + } - if (LoadOptionPtr < LoadOptionEnd) { - OptionalDataSize = BootOptionSize - - sizeof (UINT32) - - sizeof (UINT16) - - DescriptionSize - - NewLoadContext->FilePathLength; - NewLoadContext->OptionalData = AllocateZeroPool (OptionalDataSize); + UnicodeSPrint (NewMenuEntry->FilePathString, StringSize, L"File path: %s", PathString); + if (!NewLoadContext->NeedsPathExpansion) { + FREE_NON_NULL (PathString); + } + NewMenuEntry->FilePathStringToken = HiiSetString ( + Private->HiiHandle, + 0, + NewMenuEntry->FilePathString, + NULL); - if (NewLoadContext->OptionalData == NULL) { - return EFI_OUT_OF_RESOURCES; - } + FREE_NON_NULL (LoadOptionFromVar); - CopyMem ( - NewLoadContext->OptionalData, - LoadOptionPtr, - OptionalDataSize - ); - NewLoadContext->OptionalDataSize = OptionalDataSize; - } + *MenuEntry = NewMenuEntry; - // Hardware Path to the disk - HwDevicePath = StripFilePath (NewLoadContext->FilePath); - if (HwDevicePath == NULL) { - // In case there is no file device path, it means it is /EFI/BOOT/BOOTX64.efi - // and is automatically expanded by UEFI boot manager - PathString = UiDevicePathToStr (Private->DevPathToText, NewLoadContext->FilePath); - } else { - PathString = UiDevicePathToStr (Private->DevPathToText, HwDevicePath); - FREE_NON_NULL (HwDevicePath); - } - ASSERT (PathString != NULL); - StringSize = StrSize (L"Hardware path: ") + StrSize (PathString) + sizeof(CHAR16); - NewMenuEntry->DevicePathString = AllocateZeroPool (StringSize); - ASSERT (NewMenuEntry->DevicePathString != NULL); - UnicodeSPrint (NewMenuEntry->DevicePathString, StringSize, L"Hardware path: %s", PathString); - FREE_NON_NULL (PathString); - NewMenuEntry->DevicePathStringToken = HiiSetString (Private->HiiHandle, 0, NewMenuEntry->DevicePathString, NULL); - - // File path on the disk - if (HwDevicePath != NULL) { - PathString = UiDevicePathToStr (Private->DevPathToText, ExtractFilePath (NewLoadContext->FilePath)); - if (PathString == NULL) { - FreePool (HwDevicePath); - return EFI_OUT_OF_RESOURCES; - } - } else { - // In case there is no file device path, it means it is /EFI/BOOT/BOOTX64.efi - // and is automatically expanded by UEFI boot manager. However when reading - // the file in the application, we have to expand it ourselves. - PathString = EFI_REMOVABLE_MEDIA_FILE_NAME; - NewLoadContext->NeedsPathExpansion = TRUE; + return EFI_SUCCESS; +} + +/** + + Build the BootOptionMenu according to BootOrder Variable. + This Routine will access the Boot#### to get EFI_LOAD_OPTION. + + @param CallbackData The BMM context data. + + @return EFI_NOT_FOUND Fail to find "BootOrder" variable. + @return EFI_SUCESS Success build boot option menu. + +**/ +EFI_STATUS +GetBootOptions ( + IN SOVEREIGN_BOOT_WIZARD_PRIVATE_DATA *Private + ) +{ + UINTN Index; + UINT16 *BootOrderList; + UINTN BootOrderListSize; + SV_MENU_ENTRY *NewMenuEntry; + UINTN MenuCount; + EFI_BOOT_MANAGER_LOAD_OPTION *BootOption; + UINTN BootOptionCount; + EFI_STATUS Status; + + MenuCount = 0; + BootOrderListSize = 0; + BootOrderList = NULL; + InitializeListHead (&BootOptionMenu.Head); + + DEBUG ((DEBUG_INFO, "Locating boot options\n")); + + if (Private->ConfigData.AppLaunchCause == SV_BOOT_LAUNCH_IMAGE_VERIFICATION_FAILED) { + // Some boot options may not have entries returned by EfiBootManagerGetLoadOptions + // Query the Boot#### variable directly using BootCurrent index. + Status = FillMenuEntry (Private, Private->ConfigData.BootCurrent, &NewMenuEntry); + if (EFI_ERROR (Status)) { + FreeBootMenuEntry (NewMenuEntry); + return Status; } - StringSize = StrSize (L"File path: ") + StrSize (PathString) + sizeof(CHAR16); - NewMenuEntry->FilePathString = AllocateZeroPool (StringSize); - if (NewMenuEntry->FilePathString == NULL) { - if (HwDevicePath != NULL) { - FREE_NON_NULL (PathString); - FreePool (HwDevicePath); - } - return EFI_OUT_OF_RESOURCES; + + InsertTailList (&BootOptionMenu.Head, &NewMenuEntry->Link); + BootOptionMenu.MenuNumber = 1; + return EFI_SUCCESS; + } + + // + // Get the BootOrder from the Var + // + GetEfiGlobalVariable2 (L"BootOrder", (VOID **)&BootOrderList, &BootOrderListSize); + if (BootOrderList == NULL) { + return EFI_NOT_FOUND; + } + + BootOption = EfiBootManagerGetLoadOptions (&BootOptionCount, LoadOptionTypeBoot); + for (Index = 0; Index < BootOrderListSize / sizeof (UINT16); Index++) { + // + // Don't display the hidden/inactive boot option + // + if (((BootOption[Index].Attributes & LOAD_OPTION_HIDDEN) != 0) || + ((BootOption[Index].Attributes & LOAD_OPTION_ACTIVE) == 0)) { + continue; } - UnicodeSPrint (NewMenuEntry->FilePathString, StringSize, L"File path: %s", PathString); - if (HwDevicePath != NULL) { - FREE_NON_NULL (PathString); + NewMenuEntry = NULL; + Status = FillMenuEntry (Private, BootOption[Index].OptionNumber, &NewMenuEntry); + if (EFI_ERROR (Status)) { + FreeBootMenuEntry (NewMenuEntry); + if (Status == EFI_ABORTED) { + continue; + } + return Status; } - NewMenuEntry->FilePathStringToken = HiiSetString (Private->HiiHandle, 0, NewMenuEntry->FilePathString, NULL); InsertTailList (&BootOptionMenu.Head, &NewMenuEntry->Link); MenuCount++; - FreePool (LoadOptionFromVar); } EfiBootManagerFreeLoadOptions (BootOption, BootOptionCount); diff --git a/DasharoModulePkg/Application/SovereignBootWizard/SovereignBootWizard.c b/DasharoModulePkg/Application/SovereignBootWizard/SovereignBootWizard.c index 45581bbc44..72c3faca59 100644 --- a/DasharoModulePkg/Application/SovereignBootWizard/SovereignBootWizard.c +++ b/DasharoModulePkg/Application/SovereignBootWizard/SovereignBootWizard.c @@ -243,7 +243,7 @@ BootTheBootloader ( LoadOptionNumberUnassigned, LoadOptionTypeBoot, LOAD_OPTION_ACTIVE, - &BootloaderContext->Description[StrLen(L"Description: ")], + BootloaderContext->Description, BootloaderContext->FilePath, BootloaderContext->OptionalData, BootloaderContext->OptionalDataSize @@ -278,10 +278,10 @@ BootTheBootloader ( DEBUG ((DEBUG_ERROR, "Failed to add load option variable: %r\n", Status)); return Status; } - DEBUG ((DEBUG_INFO, "Booting %s\n", &BootloaderContext->Description[StrLen(L"Description: ")])); + DEBUG ((DEBUG_INFO, "Booting %s\n", BootloaderContext->Description)); EfiBootManagerBoot (&BootOption); } else { - DEBUG ((DEBUG_INFO, "Booting %s\n", &BootloaderContext->Description[StrLen(L"Description: ")])); + DEBUG ((DEBUG_INFO, "Booting %s\n", &BootloaderContext->Description)); EfiBootManagerBoot (&BootOptions[OptionIndex]); } From 20c1e776a60648533fb50e94b3fe5ab0318099ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20=C5=BBygowski?= Date: Wed, 30 Jul 2025 12:08:59 +0200 Subject: [PATCH 23/28] DMP/Application/SovereignBootWizard: Warn when trusting an untrusted cert MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Michał Żygowski --- .../SovereignBootWizard/KeyManagement.c | 20 +++++++++++++++++++ .../SovereignBootWizard/SovereignBootWizard.c | 1 + 2 files changed, 21 insertions(+) diff --git a/DasharoModulePkg/Application/SovereignBootWizard/KeyManagement.c b/DasharoModulePkg/Application/SovereignBootWizard/KeyManagement.c index 8fa956a384..e10a1242e4 100644 --- a/DasharoModulePkg/Application/SovereignBootWizard/KeyManagement.c +++ b/DasharoModulePkg/Application/SovereignBootWizard/KeyManagement.c @@ -352,6 +352,26 @@ EnrollHashToSigDB ( Status = EFI_ABORTED; goto ON_EXIT; } + if (PrivateData->ConfigData.AppLaunchCause == SV_BOOT_LAUNCH_IMAGE_VERIFICATION_FAILED) { + if (CertificateEntry->CertIsInDbx) { + do { + CreatePopUp ( + EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE, + &Key, + L"", + L"This certificate is currently untrusted.", + L"Removing certificates from DBX is not yet supported.", + L"Can not add the certificate as trusted." + L"", + L"Press ENTER to abort the process...", + L"", + NULL + ); + } while (Key.UnicodeChar != CHAR_CARRIAGE_RETURN); + Status = EFI_ABORTED; + goto ON_EXIT; + } + } } Status = SetSecureBootMode (CUSTOM_SECURE_BOOT_MODE); diff --git a/DasharoModulePkg/Application/SovereignBootWizard/SovereignBootWizard.c b/DasharoModulePkg/Application/SovereignBootWizard/SovereignBootWizard.c index 72c3faca59..15ce19df47 100644 --- a/DasharoModulePkg/Application/SovereignBootWizard/SovereignBootWizard.c +++ b/DasharoModulePkg/Application/SovereignBootWizard/SovereignBootWizard.c @@ -513,6 +513,7 @@ Callback ( L"", L"Could not find first trusted bootloader.", L"Wizard will reset the system and let firmware decide what to boot next\n", + L"", L"Press ENTER to reset the system...", L"", NULL From ca80ce802c3618a77f913f9b8e5a499c437c71b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20=C5=BBygowski?= Date: Wed, 30 Jul 2025 13:35:52 +0200 Subject: [PATCH 24/28] DMP/Application/SovereignBootWizard: Check if cert is CA MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Michał Żygowski --- .../SovereignBootWizard/KeyManagement.c | 5 +- .../SovereignBootWizard/SignatureParsing.c | 66 ++++++++++++++++++- .../SovereignBootWizard/SovereignBootWizard.h | 1 + 3 files changed, 67 insertions(+), 5 deletions(-) diff --git a/DasharoModulePkg/Application/SovereignBootWizard/KeyManagement.c b/DasharoModulePkg/Application/SovereignBootWizard/KeyManagement.c index e10a1242e4..281dba749f 100644 --- a/DasharoModulePkg/Application/SovereignBootWizard/KeyManagement.c +++ b/DasharoModulePkg/Application/SovereignBootWizard/KeyManagement.c @@ -244,9 +244,10 @@ EnrollHashToSigDB ( SigDBSize = sizeof (EFI_SIGNATURE_LIST) + sizeof (EFI_SIGNATURE_DATA) - 1; if (SecurityContext->ImageIsSigned) { - // EDK2 only checks X509 certificates in DB, so enroll whole cert to DB. + // EDK2 only checks X509 certificates in DB and DBX, so enroll whole cert to DB. + // For DBX only non-CA (signer's) certificate can be checked. // Otherwise enroll SHA256 to DBX to save space. - if (Trust) { + if (Trust || (!Trust && CertificateEntry->CertIsCA)) { SigDBSize += CertificateEntry->CertDataSize; Status = CreateSigList ( CertificateEntry->CertData, diff --git a/DasharoModulePkg/Application/SovereignBootWizard/SignatureParsing.c b/DasharoModulePkg/Application/SovereignBootWizard/SignatureParsing.c index 4f49a87354..3b4353126c 100644 --- a/DasharoModulePkg/Application/SovereignBootWizard/SignatureParsing.c +++ b/DasharoModulePkg/Application/SovereignBootWizard/SignatureParsing.c @@ -161,6 +161,63 @@ CertIsValid ( return Valid; } +STATIC BOOLEAN +CertIsCA ( + IN SV_CERT_ENTRY *CertificateEntry + ) +{ + UINT8 *Constraints; + UINTN ConstraintsSize; + + ConstraintsSize = 0; + Constraints = NULL; + + X509GetExtendedBasicConstraints (CertificateEntry->CertData, + CertificateEntry->CertDataSize, + NULL, + &ConstraintsSize); + + if (ConstraintsSize == 0) { + return FALSE; + } + + Constraints = AllocateZeroPool (ConstraintsSize); + if (Constraints == NULL) { + return FALSE; + } + + if (X509GetExtendedBasicConstraints (CertificateEntry->CertData, + CertificateEntry->CertDataSize, + Constraints, + &ConstraintsSize)) { + if (ConstraintsSize < 2) { + FreePool (Constraints); + return FALSE; + } + // If SEQUENCE byte found and length is 0, the not CA + if ((Constraints[0] == 0x30) && (Constraints[1] == 0x00)) { + FreePool (Constraints); + return FALSE; + } + if (ConstraintsSize < 5) { + FreePool (Constraints); + return FALSE; + } + // If SEQUENCE byte found and length is at least 3 + if ((Constraints[0] == 0x30) && (Constraints[1] >= 0x03)) { + // If Type is Boolean and its length is 1 then return its value + if (Constraints[2] == 0x01 && Constraints[3] == 0x01) { + FreePool (Constraints); + return (Constraints[4] != 0); + } + } + } + + FreePool (Constraints); + + return FALSE; +} + STATIC EFI_STATUS CreateNewCert ( IN SV_SECURITY_CONTEXT *SecCtx, @@ -254,6 +311,8 @@ CreateNewCert ( NewCertEntry->CertData, NewCertEntry->CertDataSize, ImageDigest, ImageDigestSize); + NewCertEntry->CertIsCA = CertIsCA (NewCertEntry); + // Mark the image as unverified if it is in DBX. if (NewCertEntry->CertIsInDbx) { SecCtx->ImageIsVerified = FALSE; @@ -264,12 +323,14 @@ CreateNewCert ( " CertIsInDb: %u\n" " CertIsInDbx: %u\n" " CertIsMicrosoft: %u\n" - " CertIsValid: %u\n", + " CertIsValid: %u\n" + " CertIsCA: %u\n", NewCertEntry->SignatureValid, NewCertEntry->CertIsInDb, NewCertEntry->CertIsInDbx, NewCertEntry->CertIsMicrosoft, - NewCertEntry->CertIsValid)); + NewCertEntry->CertIsValid, + NewCertEntry->CertIsCA)); DEBUG ((DEBUG_INFO, "Certificate hash:\n")); for (Index = 0; Index < NewCertEntry->CertDigestSize; Index++) { @@ -278,7 +339,6 @@ CreateNewCert ( DEBUG ((DEBUG_INFO, "\n")); return EFI_SUCCESS; - } VOID diff --git a/DasharoModulePkg/Application/SovereignBootWizard/SovereignBootWizard.h b/DasharoModulePkg/Application/SovereignBootWizard/SovereignBootWizard.h index 98b92bef48..15e128d7ac 100644 --- a/DasharoModulePkg/Application/SovereignBootWizard/SovereignBootWizard.h +++ b/DasharoModulePkg/Application/SovereignBootWizard/SovereignBootWizard.h @@ -214,6 +214,7 @@ typedef struct { BOOLEAN SignatureValid; BOOLEAN CertIsValid; BOOLEAN CertIsMicrosoft; + BOOLEAN CertIsCA; UINT8 CertDigest[MAX_DIGEST_SIZE]; UINTN CertDigestSize; From 164e0af873d93139aa886062d1777b356a20e7a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20=C5=BBygowski?= Date: Thu, 31 Jul 2025 12:30:22 +0200 Subject: [PATCH 25/28] DasharoPayloadPkg: Use OVMF RNG for QEMU MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Michał Żygowski --- DasharoPayloadPkg/DasharoPayloadPkg.dsc | 9 +++++++++ DasharoPayloadPkg/DasharoPayloadPkg.fdf | 4 ++++ 2 files changed, 13 insertions(+) diff --git a/DasharoPayloadPkg/DasharoPayloadPkg.dsc b/DasharoPayloadPkg/DasharoPayloadPkg.dsc index 310c174811..2ce6a7ddac 100644 --- a/DasharoPayloadPkg/DasharoPayloadPkg.dsc +++ b/DasharoPayloadPkg/DasharoPayloadPkg.dsc @@ -202,7 +202,12 @@ CacheMaintenanceLib|MdePkg/Library/BaseCacheMaintenanceLib/BaseCacheMaintenanceLib.inf SafeIntLib|MdePkg/Library/BaseSafeIntLib/BaseSafeIntLib.inf StackCheckLib|MdePkg/Library/StackCheckLibNull/StackCheckLibNull.inf +!if $(QEMU_PLATFORM) == TRUE + RngLib|MdeModulePkg/Library/BaseRngLibTimerLib/BaseRngLibTimerLib.inf + VirtioLib|OvmfPkg/Library/VirtioLib/VirtioLib.inf +!else RngLib|DasharoPayloadPkg/Library/BaseRngLib/BaseRngLib.inf +!endif NestedInterruptTplLib|OvmfPkg/Library/NestedInterruptTplLib/NestedInterruptTplLib.inf # @@ -1007,7 +1012,11 @@ OrderedCollectionLib|MdePkg/Library/BaseOrderedCollectionRedBlackTreeLib/BaseOrd # # Random Number Generator # +!if $(QEMU_PLATFORM) == TRUE + !include OvmfPkg/Include/Dsc/OvmfRngComponents.dsc.inc +!else SecurityPkg/RandomNumberGenerator/RngDxe/RngDxe.inf +!endif # # Hash2 diff --git a/DasharoPayloadPkg/DasharoPayloadPkg.fdf b/DasharoPayloadPkg/DasharoPayloadPkg.fdf index 3f28bd6cb3..e85a6b66cc 100644 --- a/DasharoPayloadPkg/DasharoPayloadPkg.fdf +++ b/DasharoPayloadPkg/DasharoPayloadPkg.fdf @@ -358,7 +358,11 @@ INF DasharoModulePkg/DasharoBootPoliciesVTd/DasharoBootPoliciesVTd.inf # # Random Number Generator # +!if $(QEMU_PLATFORM) == TRUE + !include OvmfPkg/Include/Fdf/OvmfRngDxe.fdf.inc +!else INF SecurityPkg/RandomNumberGenerator/RngDxe/RngDxe.inf +!endif # # Security From 1f21f4a6e21468916dab8b0673c992fae95979ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20=C5=BBygowski?= Date: Thu, 31 Jul 2025 13:47:07 +0200 Subject: [PATCH 26/28] DasharoModulePkg/Application/SovereignBootWizard: Fix pointer reference MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Michał Żygowski --- .../Application/SovereignBootWizard/SovereignBootWizard.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DasharoModulePkg/Application/SovereignBootWizard/SovereignBootWizard.c b/DasharoModulePkg/Application/SovereignBootWizard/SovereignBootWizard.c index 15ce19df47..1d6f5b6016 100644 --- a/DasharoModulePkg/Application/SovereignBootWizard/SovereignBootWizard.c +++ b/DasharoModulePkg/Application/SovereignBootWizard/SovereignBootWizard.c @@ -281,7 +281,7 @@ BootTheBootloader ( DEBUG ((DEBUG_INFO, "Booting %s\n", BootloaderContext->Description)); EfiBootManagerBoot (&BootOption); } else { - DEBUG ((DEBUG_INFO, "Booting %s\n", &BootloaderContext->Description)); + DEBUG ((DEBUG_INFO, "Booting %s\n", BootloaderContext->Description)); EfiBootManagerBoot (&BootOptions[OptionIndex]); } From 1ea8bcff87982ea4fafef26971e6d95add53d9f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20=C5=BBygowski?= Date: Thu, 31 Jul 2025 13:47:35 +0200 Subject: [PATCH 27/28] MdeModulePkg/Library/UefiBootManagerLib/BmBoot.c: Add missing clear screen MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Michał Żygowski --- MdeModulePkg/Library/UefiBootManagerLib/BmBoot.c | 1 + 1 file changed, 1 insertion(+) diff --git a/MdeModulePkg/Library/UefiBootManagerLib/BmBoot.c b/MdeModulePkg/Library/UefiBootManagerLib/BmBoot.c index 52e44c849c..23dd67ed77 100644 --- a/MdeModulePkg/Library/UefiBootManagerLib/BmBoot.c +++ b/MdeModulePkg/Library/UefiBootManagerLib/BmBoot.c @@ -2244,6 +2244,7 @@ EfiBootManagerBoot ( ASSERT_EFI_ERROR (Status); ASSERT (Index == 0); while (!EFI_ERROR (gST->ConIn->ReadKeyStroke (gST->ConIn, &Key))) {} + gST->ConOut->ClearScreen (gST->ConOut); } return; From 97270bcce4072b591b3526d904457d76c027cf37 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20=C5=BBygowski?= Date: Thu, 31 Jul 2025 13:48:23 +0200 Subject: [PATCH 28/28] MdeModulePkg/UefiBootManagerLib: Do not signal ReadyToBoot when launching wizard MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Michał Żygowski --- MdeModulePkg/Library/UefiBootManagerLib/BmBoot.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/MdeModulePkg/Library/UefiBootManagerLib/BmBoot.c b/MdeModulePkg/Library/UefiBootManagerLib/BmBoot.c index 23dd67ed77..100c5d79d7 100644 --- a/MdeModulePkg/Library/UefiBootManagerLib/BmBoot.c +++ b/MdeModulePkg/Library/UefiBootManagerLib/BmBoot.c @@ -2082,6 +2082,11 @@ EfiBootManagerBoot ( if (BmIsBootManagerMenuFilePath (BootOption->FilePath)) { DEBUG ((DEBUG_INFO, "[Bds] Booting Boot Manager Menu.\n")); BmStopHotkeyService (NULL, NULL); + } else if (BmIsSovereignBootWizardFilePath (BootOption->FilePath)) { + // Avoid signaling ReadyToBoot, as the wizard may call EfiBootManagerBoot + // with a proper boot option. + DEBUG ((DEBUG_INFO, "[Bds] Booting Sovereign Boot Wizard.\n")); + BmStopHotkeyService (NULL, NULL); } else { EfiSignalEventReadyToBoot (); //