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/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. 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. 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 d004536821..524b46c445 100644 --- a/DasharoModulePkg/Application/SovereignBootWizard/BootOptionParsing.c +++ b/DasharoModulePkg/Application/SovereignBootWizard/BootOptionParsing.c @@ -11,12 +11,17 @@ 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 }; +STATIC VOID +FreeBootMenuEntry ( + SV_MENU_ENTRY *BootloaderEntry + ); + /** This function converts an input device structure to a Unicode string. @@ -47,38 +52,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; @@ -108,7 +103,7 @@ StripFilePath ( } // In case we did not found file path, free the duplicated path - FreePool (Path); + FREE_NON_NULL (Path); return NULL; } @@ -139,12 +134,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 +147,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 +162,7 @@ CreateMenuEntry ( // // Create new menu entry // - MenuEntry = AllocateZeroPool (sizeof (BM_MENU_ENTRY)); + MenuEntry = AllocateZeroPool (sizeof (SV_MENU_ENTRY)); if (MenuEntry == NULL) { return NULL; } @@ -187,6 +178,223 @@ CreateMenuEntry ( return MenuEntry; } +STATIC EFI_STATUS +FillMenuEntry ( + IN SOVEREIGN_BOOT_WIZARD_PRIVATE_DATA *Private, + IN UINT16 BootOptionIndex, + IN OUT SV_MENU_ENTRY **MenuEntry + ) +{ + CHAR16 BootString[10]; + UINT8 *LoadOptionFromVar; + UINTN BootOptionSize; + UINT8 *LoadOptionPtr; + 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; + UINT8 *Ptr; + CHAR16 *PathString; + + if ((Private == NULL) || (MenuEntry == NULL)) { + return EFI_INVALID_PARAMETER; + } + + UnicodeSPrint (BootString, sizeof (BootString), L"Boot%04x", BootOptionIndex); + // + // Get all loadoptions from the VAR + // + GetEfiGlobalVariable2 (BootString, (VOID **)&LoadOptionFromVar, &BootOptionSize); + if (LoadOptionFromVar == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + Ptr = (UINT8 *)LoadOptionFromVar; + + // + // Attribute = *(UINT32 *)Ptr; + // + Ptr += sizeof (UINT32); + + // + // FilePathSize = *(UINT16 *)Ptr; + // + Ptr += sizeof (UINT16); + + // + // Description = (CHAR16 *)Ptr; + // + DescriptionSize = StrSize ((CHAR16 *)Ptr); + Ptr += DescriptionSize; + + // + // Now Ptr point to Device Path + // + DevicePath = (EFI_DEVICE_PATH_PROTOCOL *)Ptr; + + // Skip boot options that do not point to disks + if (!IsHddFilePath (DevicePath)) { + FreePool (LoadOptionFromVar); + return EFI_ABORTED; + } + + NewMenuEntry = CreateMenuEntry (SOVEREIGN_BOOT_LOAD_CONTEXT_SELECT); + if (NewMenuEntry == NULL) { + FreePool (LoadOptionFromVar); + return EFI_OUT_OF_RESOURCES; + } + + NewLoadContext = (SV_LOAD_CONTEXT *)NewMenuEntry->VariableContext; + + LoadOptionPtr = LoadOptionFromVar; + LoadOptionEnd = LoadOptionFromVar + BootOptionSize; + + NewMenuEntry->OptionNumber = BootOptionIndex; + + if ((BBS_DEVICE_PATH == DevicePath->Type) && (BBS_BBS_DP == DevicePath->SubType)) { + NewLoadContext->IsLegacy = TRUE; + } else { + NewLoadContext->IsLegacy = FALSE; + } + + // + // LoadOption is a pointer type of UINT8 + // for easy use with following LOAD_OPTION + // embedded in this struct + // + NewLoadContext->Attributes = *(UINT32 *)LoadOptionPtr; + + LoadOptionPtr += sizeof (UINT32); + + NewLoadContext->FilePathLength = *(UINT16 *)LoadOptionPtr; + LoadOptionPtr += sizeof (UINT16); + + NewLoadContext->Description = AllocateZeroPool (DescriptionSize); + if (NewLoadContext->Description == NULL) { + FreePool (LoadOptionFromVar); + return EFI_OUT_OF_RESOURCES; + } + StrCpyS (NewLoadContext->Description, DescriptionSize / sizeof(CHAR16), (CONST CHAR16 *)LoadOptionPtr); + + StringSize = StrSize (L"Description: ") + DescriptionSize; + NewMenuEntry->DisplayString = AllocateZeroPool (StringSize); + if (NewMenuEntry->DisplayString == NULL) { + FreePool (LoadOptionFromVar); + return EFI_OUT_OF_RESOURCES; + } + + UnicodeSPrint (NewMenuEntry->DisplayString, StringSize, L"Description: %s", LoadOptionPtr); + NewMenuEntry->DisplayStringToken = HiiSetString ( + Private->HiiHandle, + 0, + NewMenuEntry->DisplayString, + NULL); + + LoadOptionPtr += StrSize ((UINT16 *)LoadOptionPtr); + + NewLoadContext->FilePath = AllocateZeroPool (NewLoadContext->FilePathLength); + if (NewLoadContext->FilePath == NULL) { + FreePool (LoadOptionFromVar); + 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) { + FreePool (LoadOptionFromVar); + 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) { + // 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; + } + + 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); + + FREE_NON_NULL (LoadOptionFromVar); + + *MenuEntry = NewMenuEntry; + + return EFI_SUCCESS; +} + /** Build the BootOptionMenu according to BootOrder Variable. @@ -204,29 +412,35 @@ GetBootOptions ( ) { UINTN Index; - CHAR16 BootString[10]; - UINT8 *LoadOptionFromVar; - UINTN BootOptionSize; UINT16 *BootOrderList; UINTN BootOrderListSize; - UINT8 *LoadOptionPtr; - BM_MENU_ENTRY *NewMenuEntry; - BM_LOAD_CONTEXT *NewLoadContext; - UINTN StringSize; - EFI_DEVICE_PATH_PROTOCOL *DevicePath; - EFI_DEVICE_PATH_PROTOCOL *HwDevicePath; + SV_MENU_ENTRY *NewMenuEntry; UINTN MenuCount; - UINT8 *Ptr; EFI_BOOT_MANAGER_LOAD_OPTION *BootOption; UINTN BootOptionCount; - CHAR16 *PathString; + EFI_STATUS Status; MenuCount = 0; BootOrderListSize = 0; BootOrderList = NULL; - LoadOptionFromVar = 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; + } + + InsertTailList (&BootOptionMenu.Head, &NewMenuEntry->Link); + BootOptionMenu.MenuNumber = 1; + return EFI_SUCCESS; + } + // // Get the BootOrder from the Var // @@ -245,140 +459,25 @@ GetBootOptions ( continue; } - UnicodeSPrint (BootString, sizeof (BootString), L"Boot%04x", BootOrderList[Index]); - // - // Get all loadoptions from the VAR - // - GetEfiGlobalVariable2 (BootString, (VOID **)&LoadOptionFromVar, &BootOptionSize); - if (LoadOptionFromVar == NULL) { - continue; - } - - // - // Is a Legacy Device? - // - Ptr = (UINT8 *)LoadOptionFromVar; - - // - // Attribute = *(UINT32 *)Ptr; - // - Ptr += sizeof (UINT32); - - // - // FilePathSize = *(UINT16 *)Ptr; - // - Ptr += sizeof (UINT16); - - // - // Description = (CHAR16 *)Ptr; - // - Ptr += StrSize ((CHAR16 *)Ptr); - - // - // Now Ptr point to Device Path - // - DevicePath = (EFI_DEVICE_PATH_PROTOCOL *)Ptr; - - // Skip boot options that point to FV - if (IsFvFilePath (DevicePath)) { - FreePool (LoadOptionFromVar); - continue; - } - - NewMenuEntry = CreateMenuEntry (SOVEREIGN_BOOT_LOAD_CONTEXT_SELECT); - ASSERT (NULL != NewMenuEntry); - - NewLoadContext = (BM_LOAD_CONTEXT *)NewMenuEntry->VariableContext; - - LoadOptionPtr = LoadOptionFromVar; - - NewMenuEntry->OptionNumber = BootOrderList[Index]; - - if ((BBS_DEVICE_PATH == DevicePath->Type) && (BBS_BBS_DP == DevicePath->SubType)) { - NewLoadContext->IsLegacy = TRUE; - } else { - NewLoadContext->IsLegacy = FALSE; - } - - // - // LoadOption is a pointer type of UINT8 - // for easy use with following LOAD_OPTION - // embedded in this struct - // - - NewLoadContext->Attributes = *(UINT32 *)LoadOptionPtr; - - LoadOptionPtr += sizeof (UINT32); - - NewLoadContext->FilePathListLength = *(UINT16 *)LoadOptionPtr; - LoadOptionPtr += sizeof (UINT16); - - StringSize = StrSize (L"Description: ") + StrSize ((UINT16 *)LoadOptionPtr) + sizeof(CHAR16); - NewLoadContext->Description = AllocateZeroPool (StringSize); - ASSERT (NewLoadContext->Description != NULL); - UnicodeSPrint (NewLoadContext->Description, StringSize, L"Description: %s", LoadOptionPtr); - - NewMenuEntry->DisplayString = NewLoadContext->Description; - NewMenuEntry->DisplayStringToken = HiiSetString (Private->HiiHandle, 0, NewLoadContext->Description, NULL); - - LoadOptionPtr += StrSize ((UINT16 *)LoadOptionPtr); - - NewLoadContext->FilePathList = AllocateZeroPool (NewLoadContext->FilePathListLength); - ASSERT (NewLoadContext->FilePathList != NULL); - CopyMem ( - NewLoadContext->FilePathList, - (EFI_DEVICE_PATH_PROTOCOL *)LoadOptionPtr, - NewLoadContext->FilePathListLength - ); - - // Hardware Path to the disk - HwDevicePath = StripFilePath (NewLoadContext->FilePathList); - 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); - } else { - PathString = UiDevicePathToStr (Private->DevPathToText, HwDevicePath); - FreePool (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); - NewMenuEntry->DevicePathStringToken = HiiSetString (Private->HiiHandle, 0, NewMenuEntry->DevicePathString, NULL); - - // File path on the disk - if (HwDevicePath != NULL) { - PathString = UiDevicePathToStr (Private->DevPathToText, ExtractFilePath (NewLoadContext->FilePathList)); - 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; - } - StringSize = StrSize (L"File path: ") + StrSize (PathString) + sizeof(CHAR16); - NewMenuEntry->FilePathString = AllocateZeroPool (StringSize); - ASSERT (NewMenuEntry->FilePathString != NULL); - UnicodeSPrint (NewMenuEntry->FilePathString, StringSize, L"File path: %s", PathString); - if (HwDevicePath != NULL) { - FreePool (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); - if (BootOrderList != NULL) { - FreePool (BootOrderList); - } + FREE_NON_NULL (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; @@ -396,13 +495,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; @@ -415,28 +514,52 @@ 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; } 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; + SV_MENU_ENTRY *BootloaderEntry; EFI_STRING NewString; + EFI_STATUS Status; - BootloaderEntry = GetMenuEntry (&BootOptionMenu, OptionNumber); - 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); + 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 = (BM_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; } @@ -445,24 +568,80 @@ 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 + return Status; +} - return EFI_SUCCESS; +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 new file mode 100644 index 0000000000..281dba749f --- /dev/null +++ b/DasharoModulePkg/Application/SovereignBootWizard/KeyManagement.c @@ -0,0 +1,707 @@ +/** @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); +} + +/** 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. +**/ +STATIC 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). + + @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_CERT_X509_SHA256 SigCertHashData; + UINTN DataSize; + UINTN SigDBSize; + UINT32 Attr; + EFI_TIME Time; + SV_MENU_ENTRY *BootloaderEntry; + SV_SECURITY_CONTEXT *SecurityContext; + SV_CERT_ENTRY *CertificateEntry; + EFI_STATUS Status; + BOOLEAN Trust; + 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; + Trust = (StrCmp(VariableName, EFI_IMAGE_SECURITY_DATABASE) == 0); + + SigDBSize = sizeof (EFI_SIGNATURE_LIST) + sizeof (EFI_SIGNATURE_DATA) - 1; + + if (SecurityContext->ImageIsSigned) { + // 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 || (!Trust && CertificateEntry->CertIsCA)) { + 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, sizeof(EFI_TIME), 0); + 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 + ); + } + + if (SigDBHash == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto ON_EXIT; + } + + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + + // + // 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; + } + + 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; + } + + 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; + } + 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); + 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 (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)); + } else { + mCertIndex++; + DEBUG ((DEBUG_INFO, "Moving to next certificate %u for bootloader %u\n", mCertIndex, mBootloaderIndex)); + } + } + + Status = SetSecureBootMode (STANDARD_SECURE_BOOT_MODE); + +ON_EXIT: + + FREE_NON_NULL (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 + + 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), + 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 + ); + } + } 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. Constructing 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[0], &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: + + FREE_NON_NULL (SigList); + + if (RsaCtx != NULL) { + RsaFree (RsaCtx); + } + + return Status; +} + +STATIC 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; + } + + 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 new file mode 100644 index 0000000000..3b4353126c --- /dev/null +++ b/DasharoModulePkg/Application/SovereignBootWizard/SignatureParsing.c @@ -0,0 +1,1165 @@ +/** @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 SV_SECURITY_CONTEXT *SecCtx + ) +{ + EFI_STATUS Status; + 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; + } + + IsFound = FALSE; + Status = IsSignatureFoundInDatabase ( + 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 + ); + if (!EFI_ERROR (Status) && IsFound) { + SecCtx->ImageIsInDb = TRUE; + } + } +} + +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: + + FREE_NON_NULL (CurrentTime); + + 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, + 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); + + NewCertEntry->CertIsCA = CertIsCA (NewCertEntry); + + // 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" + " CertIsCA: %u\n", + NewCertEntry->SignatureValid, + NewCertEntry->CertIsInDb, + NewCertEntry->CertIsInDbx, + NewCertEntry->CertIsMicrosoft, + NewCertEntry->CertIsValid, + NewCertEntry->CertIsCA)); + + 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, + IN UINTN FileSize, + IN OUT SV_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 CertStatus; + SV_CERT_ENTRY *NewCertEntry; + UINT8 *CertBuffer; + UINTN BufferLength; + UINT8 *UnchainedCert; + UINTN UnchainedCertLength; + UINTN CertCount; + UINT8 *CertPtr; + UINT8 NumCert; + + 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 ((DEBUG_ERROR, "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 ((DEBUG_ERROR, "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 ((DEBUG_ERROR, "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 ((DEBUG_ERROR, "WIN Cert UEFI Data too small\n")); + break; + } + + if (!CompareGuid (&WinCertUefiGuid->CertType, &gEfiCertPkcs7Guid)) { + DEBUG ((DEBUG_ERROR, "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 ((DEBUG_ERROR, "Unknown Cert Data too small\n")); + break; + } + + DEBUG ((DEBUG_INFO, "Unknown Cert Data, skipping\n")); + continue; + } + + // 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)) { + NumCert = CertBuffer[0]; + } else { + NumCert = 0; + } + + if (NumCert == 0) { + DEBUG ((DEBUG_INFO, "Chained certificates not found\n")); + } else { + DEBUG ((DEBUG_INFO, "Found %u chained certificates\n", NumCert)); + } + + // 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 { + FREE_NON_NULL (NewCertEntry); + } + + CertPtr += *(UINT32 *)CertPtr; // Certificate size + CertPtr += sizeof (UINT32); // Certificate size field + NumCert--; + } + if (CertBuffer != NULL) { + Pkcs7FreeSigners (CertBuffer); + } + + if ((UnchainedCertLength != 0) && (UnchainedCert != NULL)) { + NumCert = UnchainedCert[0]; + } else { + NumCert = 0; + } + + if (NumCert == 0) { + DEBUG ((DEBUG_INFO, "Unchained certificates not found\n")); + } else { + DEBUG ((DEBUG_INFO, "Found %u unchained certificates\n", NumCert)); + } + + // 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)); + FREE_NON_NULL (NewCertEntry); + } + + CertPtr += *(UINT32 *)CertPtr; // Certificate size + CertPtr += sizeof (UINT32); // Certificate size field + NumCert--; + } + if (UnchainedCert != NULL) { + Pkcs7FreeSigners (UnchainedCert); + } + } + + if (Offset != SecDataDirEnd) { + DEBUG ((DEBUG_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 SV_MENU_ENTRY *Entry + ) +{ + 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; + + FullFilePath = NULL; + LoadCtx = (SV_LOAD_CONTEXT *)Entry->VariableContext; + SecCtx = (SV_SECURITY_CONTEXT *)AllocateZeroPool (sizeof(SV_SECURITY_CONTEXT)); + Entry->SecurityContext = SecCtx; + + if (SecCtx == NULL) { + 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); + } else { + FullFilePath = LoadCtx->FilePath; + } + + // + // Read the whole file content + // + Status = ReadFileContent ( + FullFilePath, + (VOID **)&ImageBase, + &ImageSize, + &AuthStatus + ); + + if (LoadCtx->NeedsPathExpansion) { + FREE_NON_NULL (FullFilePath); + } + + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "Failed to read file: %r\n", Status)); + goto ON_EXIT; + } + + Status = GetImageSecDataDir (ImageBase, ImageSize, &SecDir); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "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. + HashPeImage (ImageBase, + ImageSize, + HASHALG_SHA256, + SecCtx->ImageDigest, + &SecCtx->ImageDigestSize, + &SecCtx->HashType); + + // + // Start Image Validation. + // + VerifyImageHashInDatabases (ImageBase, ImageSize, SecCtx); + + // No certificates, end here + if ((SecDir == NULL) || (SecDir->Size == 0)) { + DEBUG ((DEBUG_INFO, "No Image Security Data, image is unsigned\n")); + SecCtx->ImageIsSigned = FALSE; + SecCtx->NumCertificates = 0; + goto ON_EXIT; + } + + 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); + FillCertificateEntries (ImageBase, ImageSize, SecCtx, SecDir); + Status = EFI_SUCCESS; + +ON_EXIT: + DEBUG ((DEBUG_INFO, "Image Details:\n" + " 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)); + + FREE_NON_NULL (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. + +**/ +SV_CERT_ENTRY * +GetCertEntry ( + SV_MENU_ENTRY *MenuEntry, + UINTN CertNumber + ) +{ + SV_CERT_ENTRY *NewCertEntry; + SV_SECURITY_CONTEXT *SecCtx; + UINTN Index; + LIST_ENTRY *List; + + SecCtx = (SV_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, SV_CERT_ENTRY, CertLink, SOVEREIGN_BOOT_CERT_ENTRY_SIGNATURE); + + return NewCertEntry; +} + +/** + 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. + @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)); + // 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++) { + 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, + IN UINTN OptionNumber + ) +{ + SV_MENU_ENTRY *BootloaderEntry; + SV_CERT_ENTRY *CertificateEntry; + SV_SECURITY_CONTEXT *SecurityContext; + EFI_STRING NewString; + EFI_STATUS Status; + + BootloaderEntry = GetMenuEntry (&BootOptionMenu, OptionNumber); + if (BootloaderEntry == NULL) { + return EFI_NO_MEDIA; + } + + SecurityContext = (SV_SECURITY_CONTEXT *)BootloaderEntry->SecurityContext; + if (SecurityContext == NULL) { + return EFI_NO_MEDIA; + } + + Private->FormData.ImageUnsigned = (!SecurityContext->ImageIsSigned || + (SecurityContext->NumCertificates == 0)); + + 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 + 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); + if (!EFI_ERROR (Status)) { + HiiSetString (Private->HiiHandle, STRING_TOKEN (STR_KEY_FINGERPRINT_HASH), NewString, NULL); + FREE_NON_NULL (NewString); + } else { + HiiSetString (Private->HiiHandle, STRING_TOKEN (STR_KEY_FINGERPRINT_HASH), L"Image hash could not be obtained.", NULL); + } + + 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); + if (CertificateEntry == NULL) { + DEBUG ((DEBUG_ERROR, "Certificate %u not found\n", mCertIndex)); + return EFI_NO_MEDIA; + } + + // 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; + } + } + + 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)); + 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)) { + HiiSetString (Private->HiiHandle, STRING_TOKEN (STR_KEY_FINGERPRINT_HASH), NewString, NULL); + FREE_NON_NULL (NewString); + } else { + HiiSetString (Private->HiiHandle, STRING_TOKEN (STR_KEY_FINGERPRINT_HASH), L"Could not obtain certificate fingerprint", NULL); + } + + return EFI_SUCCESS; +} + +VOID +UpdateCertValidityStrings ( + IN SOVEREIGN_BOOT_WIZARD_PRIVATE_DATA *Private, + IN SV_CERT_ENTRY *CertificateEntry + ) +{ + CHAR16 DateBuffer[50]; + UINT8 CertValidFrom[64]; + UINTN CertValidFromLen; + UINT8 CertValidTo[64]; + UINTN CertValidToLen; + OPENSSL_ASN1_TIME *CertTime; + + CertValidFromLen = 64; + CertValidToLen = 64; + + if (X509GetValidity(CertificateEntry->CertData, + CertificateEntry->CertDataSize, + CertValidFrom, + &CertValidFromLen, + CertValidTo, + &CertValidToLen)) { + + CertTime = (OPENSSL_ASN1_TIME *)CertValidTo; + SetMem (DateBuffer, sizeof (DateBuffer), 0); + FormatAsn1Time (CertTime, DateBuffer, sizeof (DateBuffer)); + HiiSetString (Private->HiiHandle, STRING_TOKEN (STR_VALIDITY_AFTER_DATE), DateBuffer, NULL); + + CertTime = (OPENSSL_ASN1_TIME *)CertValidFrom; + SetMem (DateBuffer, sizeof (DateBuffer), 0); + FormatAsn1Time (CertTime, DateBuffer, sizeof (DateBuffer)); + 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); + FREE_NON_NULL (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")); + goto ON_EXIT; + } + + ModulusBuffer = (UINT8 *) AllocateZeroPool (PubKeyModSize); + if (ModulusBuffer == NULL) { + DEBUG ((DEBUG_ERROR, "Could not allocate memory for modulus\n")); + goto ON_EXIT; + } + + 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; + } + + NewString = NULL; + if (!EFI_ERROR (ParseKeyModulus (ModulusBuffer, PubKeyModSize, &NewString))) { + HiiSetString (Private->HiiHandle, STRING_TOKEN (STR_CERT_KEY_MODULUS_HEX), NewString, NULL); + } + FREE_NON_NULL (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); + FREE_NON_NULL (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; + } + + UpdateCertValidityStrings (Private, CertificateEntry); + UpdateCertIssuerAndSubjectStrings (Private, CertificateEntry); + UpdateCertSerialNumberString (Private, CertificateEntry); + UpdateCertKeyStrings (Private, CertificateEntry); + + 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 d4df2a138f..1d6f5b6016 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,7 +39,9 @@ STATIC HII_VENDOR_DEVICE_PATH mHiiVendorDevicePath = { } }; -STATIC UINTN mBootloaderIndex = 0; +UINTN mBootloaderIndex; +UINTN mCertIndex; +INTN mFirstTrustedBootloader; EFI_STATUS EFIAPI @@ -119,7 +121,7 @@ ExtractConfig ( ConfigRequestHdr, (UINT64) BufferSize ); - FreePool (ConfigRequestHdr); + FREE_NON_NULL (ConfigRequestHdr); } // Convert fields of binary structure to string representation. @@ -210,6 +212,85 @@ RouteConfig ( return EFI_SUCCESS; } +EFI_STATUS +BootTheBootloader ( + SOVEREIGN_BOOT_WIZARD_PRIVATE_DATA *PrivateData, + UINTN BootloaderIndex + ) +{ + 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; + } + + Status = EfiBootManagerInitializeLoadOption ( + &BootOption, + LoadOptionNumberUnassigned, + LoadOptionTypeBoot, + LOAD_OPTION_ACTIVE, + BootloaderContext->Description, + BootloaderContext->FilePath, + BootloaderContext->OptionalData, + BootloaderContext->OptionalDataSize + ); + + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "Failed to prepare load option: %r\n", Status)); + 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)); + EfiBootManagerBoot (&BootOption); + } else { + DEBUG ((DEBUG_INFO, "Booting %s\n", BootloaderContext->Description)); + EfiBootManagerBoot (&BootOptions[OptionIndex]); + } + + EfiBootManagerFreeLoadOption (&BootOption); + EfiBootManagerFreeLoadOptions (BootOptions, BootOptionCount); + + return EFI_SUCCESS; +} + /** This function processes the results of changes in configuration. @@ -244,8 +325,12 @@ 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; + BROWSER_SETTING_SCOPE Scope; + UINTN BootloaderToBoot; if (((Value == NULL) && (Action != EFI_BROWSER_ACTION_FORM_OPEN) && (Action != EFI_BROWSER_ACTION_FORM_CLOSE)) || (ActionRequest == NULL)) @@ -256,29 +341,80 @@ 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) { 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; } + 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; + } + case DO_NOT_TRUST_KEY_FORM2_QUESTION_ID: + { + // Add cert or image hash to DBX + Status = AddKeyOrHashAsTrustedOrUntrusted(PrivateData, FALSE); + break; + } + case TRUST_KEY_FORM2_QUESTION_ID: + { + // Add cert or image hash to DB + 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; } @@ -291,44 +427,118 @@ 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 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) { - Status = UpdateBootloaderPage (PrivateData, ++mBootloaderIndex); + 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, &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"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 boto 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. - 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) { + // 3. Set the SV boot variable to provisioned state. + // 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"", + 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, @@ -336,7 +546,7 @@ Callback ( L"", L"Could not get bootloader description.", L"", - L"Press ENTER to continue ...", + L"Press ENTER to continue...", L"", NULL ); @@ -354,8 +564,77 @@ Callback ( ActionRequest ); } + } + 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 (); + 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 + ); + 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 { - Status = EFI_NO_MEDIA; + // 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: @@ -368,17 +647,12 @@ 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)); if (!EFI_ERROR (Status)) { mBootloadersInitted = TRUE; if (!mBootloadersShown) { - Status = UpdateBootloaderPage (PrivateData, mBootloaderIndex); - DEBUG ((EFI_D_INFO, "UpdateBootloaderPage(%d): %r\n", mBootloaderIndex, Status)); + Status = UpdateBootloaderPage (PrivateData); mBootloadersShown = !EFI_ERROR (Status); } } else { @@ -389,7 +663,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 ); @@ -407,7 +681,6 @@ Callback ( } } break; - default: break; } @@ -419,6 +692,13 @@ Callback ( break; } + HiiSetBrowserData ( + &gSovereignBootWizardFormSetGuid, + mVarStoreName, + sizeof (SOVEREIGN_BOOT_WIZARD_FORM_DATA), + (CONST UINT8 *)&PrivateData->FormData, + NULL); + return Status; } @@ -438,7 +718,6 @@ SovereignBootWizardInit ( IN EFI_SYSTEM_TABLE *SystemTable ) { - EFI_STATUS FormBrowserStatus; EFI_STATUS Status; EFI_HII_HANDLE HiiHandle; EFI_SCREEN_DESCRIPTOR Screen; @@ -449,8 +728,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; @@ -515,6 +794,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 // @@ -549,7 +835,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; } @@ -564,7 +850,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; @@ -622,6 +916,14 @@ 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. + ConfigData->AlreadyStarted = TRUE; + 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 || @@ -637,6 +939,19 @@ 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 + // 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 @@ -672,15 +987,20 @@ 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); } + 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; @@ -709,7 +1029,7 @@ SovereignBootWizardInit ( ASSERT_EFI_ERROR (Status); - if (!EFI_ERROR (FormBrowserStatus)) { + if (mPrivateData->FormBrowserEx2 != NULL) { // // Register the default HotKey F9 and F10 again. // @@ -717,14 +1037,14 @@ 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); - FreePool (NewString); + mPrivateData->FormBrowserEx2->RegisterHotKey (&HotKey, BROWSER_ACTION_SUBMIT, 0, NewString); + FREE_NON_NULL (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); - FreePool (NewString); + mPrivateData->FormBrowserEx2->RegisterHotKey (&HotKey, BROWSER_ACTION_DEFAULT, EFI_HII_DEFAULT_CLASS_STANDARD, NewString); + FREE_NON_NULL (NewString); } Status = SovereignBootWizardUnload (ImageHandle); @@ -766,12 +1086,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]); } - 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 4b69ea7046..15e128d7ac 100644 --- a/DasharoModulePkg/Application/SovereignBootWizard/SovereignBootWizard.h +++ b/DasharoModulePkg/Application/SovereignBootWizard/SovereignBootWizard.h @@ -20,6 +20,7 @@ Revision History #include +#include #include #include @@ -36,12 +37,15 @@ Revision History #include #include #include +#include #include #include #include #include +#include +#include #include #include #include @@ -55,7 +59,7 @@ Revision History #include #include #include - +#include #include "SovereignBootWizardHii.h" @@ -69,20 +73,28 @@ 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 +#define FREE_NON_NULL(Pointer) \ + do { \ + if ((Pointer) != NULL) { \ + FreePool((Pointer)); \ + (Pointer) = NULL; \ + } \ + } while(FALSE) + typedef struct { UINTN Signature; 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]; @@ -94,6 +106,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; @@ -109,13 +122,59 @@ 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() +#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 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; LIST_ENTRY Head; UINTN MenuNumber; -} BM_MENU_OPTION; +} SV_MENU_OPTION; typedef struct { UINTN Signature; @@ -128,53 +187,158 @@ typedef struct { EFI_STRING_ID DevicePathStringToken; EFI_STRING_ID FilePathStringToken; UINTN ContextSelection; - VOID *VariableContext; -} BM_MENU_ENTRY; + VOID *VariableContext; // SV_LOAD_CONTEXT + VOID *SecurityContext; // SV_SECURITY_CONTEXT +} SV_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; -} BM_LOAD_CONTEXT; + EFI_DEVICE_PATH_PROTOCOL *FilePath; + UINT8 *OptionalData; + UINTN OptionalDataSize; +} SV_LOAD_CONTEXT; + +typedef struct { + UINT32 Signature; + + BOOLEAN CertIsInDbx; + BOOLEAN CertIsInDb; + BOOLEAN SignatureValid; + BOOLEAN CertIsValid; + BOOLEAN CertIsMicrosoft; + BOOLEAN CertIsCA; + + UINT8 CertDigest[MAX_DIGEST_SIZE]; + UINTN CertDigestSize; + + UINTN CertDataSize; + UINT8 *CertData; + + EFI_GUID CertType; + + LIST_ENTRY CertLink; +} SV_CERT_ENTRY; typedef struct { - EFI_HANDLE Handle; - EFI_DEVICE_PATH_PROTOCOL *DevicePath; - EFI_FILE_HANDLE FHandle; - UINT16 *FileName; - EFI_FILE_SYSTEM_VOLUME_LABEL *Info; + UINT8 ImageDigest[MAX_DIGEST_SIZE]; + UINTN ImageDigestSize; - BOOLEAN IsRoot; - BOOLEAN IsDir; - BOOLEAN IsRemovableMedia; - BOOLEAN IsLoadFile; - BOOLEAN IsBootLegacy; -} BM_FILE_CONTEXT; + BOOLEAN ImageIsInDbx; + BOOLEAN ImageIsInDb; + BOOLEAN ImageIsSigned; + BOOLEAN ImageIsVerified; -extern BM_MENU_OPTION BootOptionMenu; + UINT32 AuthenticationStatus; + UINT32 NumCertificates; + + EFI_GUID HashType; + + LIST_ENTRY Certs; +} SV_SECURITY_CONTEXT; + +extern SV_MENU_OPTION BootOptionMenu; +extern UINTN mBootloaderIndex; +extern UINTN mCertIndex; +extern INTN mFirstTrustedBootloader; EFI_STATUS 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 ); EFI_STATUS UpdateBootloaderPage ( + IN SOVEREIGN_BOOT_WIZARD_PRIVATE_DATA *Private + ); + +EFI_STATUS +FillSecurityContext ( + IN SV_MENU_ENTRY *Entry + ); + +SV_CERT_ENTRY * +GetCertEntry ( + SV_MENU_ENTRY *MenuEntry, + UINTN CertNumber + ); + +EFI_STATUS +UpdateCertInfo ( IN SOVEREIGN_BOOT_WIZARD_PRIVATE_DATA *Private, - IN UINTN OptionNumber + IN UINTN OptionNumber + ); + +EFI_STATUS +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 + ); + +EFI_STATUS +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 + ); + +VOID +FreeBootMenuEntries ( + VOID + ); + +VOID +FreeSecurityContext ( + SV_SECURITY_CONTEXT *SecCtx ); #endif diff --git a/DasharoModulePkg/Application/SovereignBootWizard/SovereignBootWizard.inf b/DasharoModulePkg/Application/SovereignBootWizard/SovereignBootWizard.inf index 9580849b00..56f4589dc3 100644 --- a/DasharoModulePkg/Application/SovereignBootWizard/SovereignBootWizard.inf +++ b/DasharoModulePkg/Application/SovereignBootWizard/SovereignBootWizard.inf @@ -26,7 +26,10 @@ # [Sources] + Asn1Time.c BootOptionParsing.c + KeyManagement.c + SignatureParsing.c SovereignBootWizard.c SovereignBootWizardHii.h SovereignBootWizardVfrStrings.uni @@ -38,12 +41,14 @@ MdeModulePkg/MdeModulePkg.dec DasharoModulePkg/DasharoModulePkg.dec SecurityPkg/SecurityPkg.dec + CryptoPkg/CryptoPkg.dec [LibraryClasses] BaseLib BaseCryptLib MemoryAllocationLib UefiBootManagerLib + DxeServicesLib UefiBootServicesTableLib UefiApplicationEntryPoint UefiRuntimeServicesTableLib @@ -57,13 +62,18 @@ PeCoffLib SecureBootVariableLib SecureBootVariableProvisionLib + DxeImageVerificationLib [Guids] gSovereignBootWizardFormSetGuid + gEfiCertRsa2048Guid + gEfiCertPkcs7Guid + gEfiCertX509Sha256Guid + gEfiImageSecurityDatabaseGuid + gEfiGlobalVariableGuid [Protocols] ## PRODUCES # SovereignBootWizardFormSet - ## PRODUCES # SovereignBootWizardInventory gEfiDevicePathProtocolGuid gEfiHiiStringProtocolGuid ## CONSUMES gEfiHiiConfigRoutingProtocolGuid ## CONSUMES @@ -76,6 +86,7 @@ gEfiHiiPopupProtocolGuid ## CONSUMES gEfiDevicePathToTextProtocolGuid ## CONSUMES gEfiFirmwareVolume2ProtocolGuid ## CONSUMES + gEfiRealTimeClockArchProtocolGuid ## SOMETIMES_CONSUMES [UserExtensions.TianoCore."ExtraFiles"] SovereignBootWizardExtra.uni diff --git a/DasharoModulePkg/Application/SovereignBootWizard/SovereignBootWizardHii.h b/DasharoModulePkg/Application/SovereignBootWizard/SovereignBootWizardHii.h index f5b82fd765..be5f426788 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 @@ -36,7 +37,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 @@ -45,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 @@ -57,7 +59,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..6a1bba1b7f 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); @@ -132,14 +133,26 @@ formset text = STRING_TOKEN(STR_TRUST_KEY), flags = INTERACTIVE, key = TRUST_KEY_FORM2_QUESTION_ID; - endif; text help = STRING_TOKEN(STR_EMPTY_STRING), - text = STRING_TOKEN(STR_SHOW_KEY_DETAILS), + text = STRING_TOKEN(STR_SKIP_KEY), + flags = INTERACTIVE, + key = SKIP_KEY_FORM2_QUESTION_ID; + endif; + + 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), flags = INTERACTIVE, key = SHOW_KEY_DETAILS_FORM2_QUESTION_ID; + subtitle text = STRING_TOKEN(STR_EMPTY_STRING); + endif; + text help = STRING_TOKEN(STR_EMPTY_STRING), text = STRING_TOKEN(STR_EXIT_TEXT), @@ -156,6 +169,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 a1700eb372..15d3546355 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" @@ -61,15 +61,41 @@ #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?" +#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" -#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 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 +#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" diff --git a/DasharoModulePkg/Include/Guid/SovereignBoot.h b/DasharoModulePkg/Include/Guid/SovereignBoot.h index e84eaeae48..f46091c5cf 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; + BOOLEAN AlreadyStarted; + UINT16 BootCurrent; } SOVEREIGN_BOOT_WIZARD_CONFIG_DATA; // State of SV Boot in EFI variables (non-volatile, BS access) diff --git a/DasharoPayloadPkg/DasharoPayloadPkg.dsc b/DasharoPayloadPkg/DasharoPayloadPkg.dsc index 9bfb1c3b7e..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 # @@ -363,6 +368,8 @@ AuthVariableLib|MdeModulePkg/Library/AuthVariableLibNull/AuthVariableLibNull.inf !endif + DxeImageVerificationLib|SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.inf + # # SMMSTORE # @@ -770,7 +777,10 @@ OrderedCollectionLib|MdePkg/Library/BaseOrderedCollectionRedBlackTreeLib/BaseOrd MdeModulePkg/Universal/SecurityStubDxe/SecurityStubDxe.inf { !if $(SECURE_BOOT_ENABLE) == TRUE - NULL|SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.inf + # 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 NULL|SecurityPkg/Library/DxeTpmMeasureBootLib/DxeTpmMeasureBootLib.inf @@ -783,7 +793,12 @@ 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 { + + # 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 @@ -997,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 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/MdeModulePkg/Library/UefiBootManagerLib/BmBoot.c b/MdeModulePkg/Library/UefiBootManagerLib/BmBoot.c index 016a0dad80..100c5d79d7 100644 --- a/MdeModulePkg/Library/UefiBootManagerLib/BmBoot.c +++ b/MdeModulePkg/Library/UefiBootManagerLib/BmBoot.c @@ -1862,11 +1862,34 @@ EfiBootManagerLaunchSovereignBootWizard ( EFI_BOOT_MANAGER_LOAD_OPTION BootOption; EFI_DEVICE_PATH_PROTOCOL *FilePath; SOVEREIGN_BOOT_WIZARD_CONFIG_DATA SvBootData; + UINT16 *BootCurrent; + UINTN BootCurrentSize; + UINTN DataSize; + + BootCurrent = NULL; + BootCurrentSize = 0; if (!FixedPcdGetBool (PcdSovereignBootEnabled)) { 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; @@ -1874,7 +1897,7 @@ EfiBootManagerLaunchSovereignBootWizard ( Status = EfiBootManagerInitializeLoadOption ( &BootOption, - 0, + LoadOptionNumberUnassigned, LoadOptionTypeBoot, LOAD_OPTION_ACTIVE | LOAD_OPTION_CATEGORY_APP, L"Sovereign Boot Wizard", @@ -1885,6 +1908,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 // @@ -1898,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 // @@ -2037,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 (); // @@ -2136,8 +2186,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; @@ -2192,6 +2249,7 @@ EfiBootManagerBoot ( ASSERT_EFI_ERROR (Status); ASSERT (Index == 0); while (!EFI_ERROR (gST->ConIn->ReadKeyStroke (gST->ConIn, &Key))) {} + gST->ConOut->ClearScreen (gST->ConOut); } return; 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..ceb02b2000 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,10 @@ MdeModulePkg/Universal/SecurityStubDxe/SecurityStubDxe.inf { !if $(SECURE_BOOT_ENABLE) == TRUE - NULL|SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.inf + # 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 } @@ -1087,7 +1091,12 @@ OvmfPkg/EnrollDefaultKeys/EnrollDefaultKeys.inf !if $(SOVEREIGN_BOOT_ENABLE) == TRUE - DasharoModulePkg/Application/SovereignBootWizard/SovereignBootWizard.inf + DasharoModulePkg/Application/SovereignBootWizard/SovereignBootWizard.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/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