summaryrefslogtreecommitdiffstats
path: root/src/VBox/Devices/EFI/Firmware/SecurityPkg/Library/AuthVariableLib/AuthService.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/VBox/Devices/EFI/Firmware/SecurityPkg/Library/AuthVariableLib/AuthService.c')
-rw-r--r--src/VBox/Devices/EFI/Firmware/SecurityPkg/Library/AuthVariableLib/AuthService.c2318
1 files changed, 2318 insertions, 0 deletions
diff --git a/src/VBox/Devices/EFI/Firmware/SecurityPkg/Library/AuthVariableLib/AuthService.c b/src/VBox/Devices/EFI/Firmware/SecurityPkg/Library/AuthVariableLib/AuthService.c
new file mode 100644
index 00000000..b035a343
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/SecurityPkg/Library/AuthVariableLib/AuthService.c
@@ -0,0 +1,2318 @@
+/** @file
+ Implement authentication services for the authenticated variables.
+
+ Caution: This module requires additional review when modified.
+ This driver will have external input - variable data. It may be input in SMM mode.
+ This external input must be validated carefully to avoid security issue like
+ buffer overflow, integer overflow.
+ Variable attribute should also be checked to avoid authentication bypass.
+ The whole SMM authentication variable design relies on the integrity of flash part and SMM.
+ which is assumed to be protected by platform. All variable code and metadata in flash/SMM Memory
+ may not be modified without authorization. If platform fails to protect these resources,
+ the authentication service provided in this driver will be broken, and the behavior is undefined.
+
+ ProcessVarWithPk(), ProcessVarWithKek() and ProcessVariable() are the function to do
+ variable authentication.
+
+ VerifyTimeBasedPayloadAndUpdate() and VerifyCounterBasedPayload() are sub function to do verification.
+ They will do basic validation for authentication data structure, then call crypto library
+ to verify the signature.
+
+Copyright (c) 2009 - 2019, Intel Corporation. All rights reserved.<BR>
+Copyright (c) Microsoft Corporation.
+SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include "AuthServiceInternal.h"
+
+#include <Protocol/VariablePolicy.h>
+#include <Library/VariablePolicyLib.h>
+
+//
+// Public Exponent of RSA Key.
+//
+CONST UINT8 mRsaE[] = { 0x01, 0x00, 0x01 };
+
+CONST UINT8 mSha256OidValue[] = { 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01 };
+
+//
+// Requirement for different signature type which have been defined in UEFI spec.
+// These data are used to perform SignatureList format check while setting PK/KEK variable.
+//
+EFI_SIGNATURE_ITEM mSupportSigItem[] = {
+//{SigType, SigHeaderSize, SigDataSize }
+ {EFI_CERT_SHA256_GUID, 0, 32 },
+ {EFI_CERT_RSA2048_GUID, 0, 256 },
+ {EFI_CERT_RSA2048_SHA256_GUID, 0, 256 },
+ {EFI_CERT_SHA1_GUID, 0, 20 },
+ {EFI_CERT_RSA2048_SHA1_GUID, 0, 256 },
+ {EFI_CERT_X509_GUID, 0, ((UINT32) ~0)},
+ {EFI_CERT_SHA224_GUID, 0, 28 },
+ {EFI_CERT_SHA384_GUID, 0, 48 },
+ {EFI_CERT_SHA512_GUID, 0, 64 },
+ {EFI_CERT_X509_SHA256_GUID, 0, 48 },
+ {EFI_CERT_X509_SHA384_GUID, 0, 64 },
+ {EFI_CERT_X509_SHA512_GUID, 0, 80 }
+};
+
+/**
+ Finds variable in storage blocks of volatile and non-volatile storage areas.
+
+ This code finds variable in storage blocks of volatile and non-volatile storage areas.
+ If VariableName is an empty string, then we just return the first
+ qualified variable without comparing VariableName and VendorGuid.
+
+ @param[in] VariableName Name of the variable to be found.
+ @param[in] VendorGuid Variable vendor GUID to be found.
+ @param[out] Data Pointer to data address.
+ @param[out] DataSize Pointer to data size.
+
+ @retval EFI_INVALID_PARAMETER If VariableName is not an empty string,
+ while VendorGuid is NULL.
+ @retval EFI_SUCCESS Variable successfully found.
+ @retval EFI_NOT_FOUND Variable not found
+
+**/
+EFI_STATUS
+AuthServiceInternalFindVariable (
+ IN CHAR16 *VariableName,
+ IN EFI_GUID *VendorGuid,
+ OUT VOID **Data,
+ OUT UINTN *DataSize
+ )
+{
+ EFI_STATUS Status;
+ AUTH_VARIABLE_INFO AuthVariableInfo;
+
+ ZeroMem (&AuthVariableInfo, sizeof (AuthVariableInfo));
+ Status = mAuthVarLibContextIn->FindVariable (
+ VariableName,
+ VendorGuid,
+ &AuthVariableInfo
+ );
+ *Data = AuthVariableInfo.Data;
+ *DataSize = AuthVariableInfo.DataSize;
+ return Status;
+}
+
+/**
+ Update the variable region with Variable information.
+
+ @param[in] VariableName Name of variable.
+ @param[in] VendorGuid Guid of variable.
+ @param[in] Data Data pointer.
+ @param[in] DataSize Size of Data.
+ @param[in] Attributes Attribute value of the variable.
+
+ @retval EFI_SUCCESS The update operation is success.
+ @retval EFI_INVALID_PARAMETER Invalid parameter.
+ @retval EFI_WRITE_PROTECTED Variable is write-protected.
+ @retval EFI_OUT_OF_RESOURCES There is not enough resource.
+
+**/
+EFI_STATUS
+AuthServiceInternalUpdateVariable (
+ IN CHAR16 *VariableName,
+ IN EFI_GUID *VendorGuid,
+ IN VOID *Data,
+ IN UINTN DataSize,
+ IN UINT32 Attributes
+ )
+{
+ AUTH_VARIABLE_INFO AuthVariableInfo;
+
+ ZeroMem (&AuthVariableInfo, sizeof (AuthVariableInfo));
+ AuthVariableInfo.VariableName = VariableName;
+ AuthVariableInfo.VendorGuid = VendorGuid;
+ AuthVariableInfo.Data = Data;
+ AuthVariableInfo.DataSize = DataSize;
+ AuthVariableInfo.Attributes = Attributes;
+
+ return mAuthVarLibContextIn->UpdateVariable (
+ &AuthVariableInfo
+ );
+}
+
+/**
+ Update the variable region with Variable information.
+
+ @param[in] VariableName Name of variable.
+ @param[in] VendorGuid Guid of variable.
+ @param[in] Data Data pointer.
+ @param[in] DataSize Size of Data.
+ @param[in] Attributes Attribute value of the variable.
+ @param[in] TimeStamp Value of associated TimeStamp.
+
+ @retval EFI_SUCCESS The update operation is success.
+ @retval EFI_INVALID_PARAMETER Invalid parameter.
+ @retval EFI_WRITE_PROTECTED Variable is write-protected.
+ @retval EFI_OUT_OF_RESOURCES There is not enough resource.
+
+**/
+EFI_STATUS
+AuthServiceInternalUpdateVariableWithTimeStamp (
+ IN CHAR16 *VariableName,
+ IN EFI_GUID *VendorGuid,
+ IN VOID *Data,
+ IN UINTN DataSize,
+ IN UINT32 Attributes,
+ IN EFI_TIME *TimeStamp
+ )
+{
+ EFI_STATUS FindStatus;
+ VOID *OrgData;
+ UINTN OrgDataSize;
+ AUTH_VARIABLE_INFO AuthVariableInfo;
+
+ FindStatus = AuthServiceInternalFindVariable (
+ VariableName,
+ VendorGuid,
+ &OrgData,
+ &OrgDataSize
+ );
+
+ //
+ // EFI_VARIABLE_APPEND_WRITE attribute only effects for existing variable
+ //
+ if (!EFI_ERROR (FindStatus) && ((Attributes & EFI_VARIABLE_APPEND_WRITE) != 0)) {
+ if ((CompareGuid (VendorGuid, &gEfiImageSecurityDatabaseGuid) &&
+ ((StrCmp (VariableName, EFI_IMAGE_SECURITY_DATABASE) == 0) || (StrCmp (VariableName, EFI_IMAGE_SECURITY_DATABASE1) == 0) ||
+ (StrCmp (VariableName, EFI_IMAGE_SECURITY_DATABASE2) == 0))) ||
+ (CompareGuid (VendorGuid, &gEfiGlobalVariableGuid) && (StrCmp (VariableName, EFI_KEY_EXCHANGE_KEY_NAME) == 0))) {
+ //
+ // For variables with formatted as EFI_SIGNATURE_LIST, the driver shall not perform an append of
+ // EFI_SIGNATURE_DATA values that are already part of the existing variable value.
+ //
+ FilterSignatureList (
+ OrgData,
+ OrgDataSize,
+ Data,
+ &DataSize
+ );
+ }
+ }
+
+ ZeroMem (&AuthVariableInfo, sizeof (AuthVariableInfo));
+ AuthVariableInfo.VariableName = VariableName;
+ AuthVariableInfo.VendorGuid = VendorGuid;
+ AuthVariableInfo.Data = Data;
+ AuthVariableInfo.DataSize = DataSize;
+ AuthVariableInfo.Attributes = Attributes;
+ AuthVariableInfo.TimeStamp = TimeStamp;
+ return mAuthVarLibContextIn->UpdateVariable (
+ &AuthVariableInfo
+ );
+}
+
+/**
+ Determine whether this operation needs a physical present user.
+
+ @param[in] VariableName Name of the Variable.
+ @param[in] VendorGuid GUID of the Variable.
+
+ @retval TRUE This variable is protected, only a physical present user could set this variable.
+ @retval FALSE This variable is not protected.
+
+**/
+BOOLEAN
+NeedPhysicallyPresent(
+ IN CHAR16 *VariableName,
+ IN EFI_GUID *VendorGuid
+ )
+{
+ // If the VariablePolicy engine is disabled, allow deletion of any authenticated variables.
+ if (IsVariablePolicyEnabled()) {
+ if ((CompareGuid (VendorGuid, &gEfiSecureBootEnableDisableGuid) && (StrCmp (VariableName, EFI_SECURE_BOOT_ENABLE_NAME) == 0))
+ || (CompareGuid (VendorGuid, &gEfiCustomModeEnableGuid) && (StrCmp (VariableName, EFI_CUSTOM_MODE_NAME) == 0))) {
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+/**
+ Determine whether the platform is operating in Custom Secure Boot mode.
+
+ @retval TRUE The platform is operating in Custom mode.
+ @retval FALSE The platform is operating in Standard mode.
+
+**/
+BOOLEAN
+InCustomMode (
+ VOID
+ )
+{
+ EFI_STATUS Status;
+ VOID *Data;
+ UINTN DataSize;
+
+ Status = AuthServiceInternalFindVariable (EFI_CUSTOM_MODE_NAME, &gEfiCustomModeEnableGuid, &Data, &DataSize);
+ if (!EFI_ERROR (Status) && (*(UINT8 *) Data == CUSTOM_SECURE_BOOT_MODE)) {
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+/**
+ Update platform mode.
+
+ @param[in] Mode SETUP_MODE or USER_MODE.
+
+ @return EFI_INVALID_PARAMETER Invalid parameter.
+ @return EFI_SUCCESS Update platform mode successfully.
+
+**/
+EFI_STATUS
+UpdatePlatformMode (
+ IN UINT32 Mode
+ )
+{
+ EFI_STATUS Status;
+ VOID *Data;
+ UINTN DataSize;
+ UINT8 SecureBootMode;
+ UINT8 SecureBootEnable;
+ UINTN VariableDataSize;
+
+ Status = AuthServiceInternalFindVariable (
+ EFI_SETUP_MODE_NAME,
+ &gEfiGlobalVariableGuid,
+ &Data,
+ &DataSize
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // Update the value of SetupMode variable by a simple mem copy, this could avoid possible
+ // variable storage reclaim at runtime.
+ //
+ mPlatformMode = (UINT8) Mode;
+ CopyMem (Data, &mPlatformMode, sizeof(UINT8));
+
+ if (mAuthVarLibContextIn->AtRuntime ()) {
+ //
+ // SecureBoot Variable indicates whether the platform firmware is operating
+ // in Secure boot mode (1) or not (0), so we should not change SecureBoot
+ // Variable in runtime.
+ //
+ return Status;
+ }
+
+ //
+ // Check "SecureBoot" variable's existence.
+ // If it doesn't exist, firmware has no capability to perform driver signing verification,
+ // then set "SecureBoot" to 0.
+ //
+ Status = AuthServiceInternalFindVariable (
+ EFI_SECURE_BOOT_MODE_NAME,
+ &gEfiGlobalVariableGuid,
+ &Data,
+ &DataSize
+ );
+ //
+ // If "SecureBoot" variable exists, then check "SetupMode" variable update.
+ // If "SetupMode" variable is USER_MODE, "SecureBoot" variable is set to 1.
+ // If "SetupMode" variable is SETUP_MODE, "SecureBoot" variable is set to 0.
+ //
+ if (EFI_ERROR (Status)) {
+ SecureBootMode = SECURE_BOOT_MODE_DISABLE;
+ } else {
+ if (mPlatformMode == USER_MODE) {
+ SecureBootMode = SECURE_BOOT_MODE_ENABLE;
+ } else if (mPlatformMode == SETUP_MODE) {
+ SecureBootMode = SECURE_BOOT_MODE_DISABLE;
+ } else {
+ return EFI_NOT_FOUND;
+ }
+ }
+
+ Status = AuthServiceInternalUpdateVariable (
+ EFI_SECURE_BOOT_MODE_NAME,
+ &gEfiGlobalVariableGuid,
+ &SecureBootMode,
+ sizeof(UINT8),
+ EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_BOOTSERVICE_ACCESS
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // Check "SecureBootEnable" variable's existence. It can enable/disable secure boot feature.
+ //
+ Status = AuthServiceInternalFindVariable (
+ EFI_SECURE_BOOT_ENABLE_NAME,
+ &gEfiSecureBootEnableDisableGuid,
+ &Data,
+ &DataSize
+ );
+
+ if (SecureBootMode == SECURE_BOOT_MODE_ENABLE) {
+ //
+ // Create the "SecureBootEnable" variable as secure boot is enabled.
+ //
+ SecureBootEnable = SECURE_BOOT_ENABLE;
+ VariableDataSize = sizeof (SecureBootEnable);
+ } else {
+ //
+ // Delete the "SecureBootEnable" variable if this variable exist as "SecureBoot"
+ // variable is not in secure boot state.
+ //
+ if (EFI_ERROR (Status)) {
+ return EFI_SUCCESS;
+ }
+ SecureBootEnable = SECURE_BOOT_DISABLE;
+ VariableDataSize = 0;
+ }
+
+ Status = AuthServiceInternalUpdateVariable (
+ EFI_SECURE_BOOT_ENABLE_NAME,
+ &gEfiSecureBootEnableDisableGuid,
+ &SecureBootEnable,
+ VariableDataSize,
+ EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS
+ );
+ return Status;
+}
+
+/**
+ Check input data form to make sure it is a valid EFI_SIGNATURE_LIST for PK/KEK/db/dbx/dbt variable.
+
+ @param[in] VariableName Name of Variable to be check.
+ @param[in] VendorGuid Variable vendor GUID.
+ @param[in] Data Point to the variable data to be checked.
+ @param[in] DataSize Size of Data.
+
+ @return EFI_INVALID_PARAMETER Invalid signature list format.
+ @return EFI_SUCCESS Passed signature list format check successfully.
+
+**/
+EFI_STATUS
+CheckSignatureListFormat(
+ IN CHAR16 *VariableName,
+ IN EFI_GUID *VendorGuid,
+ IN VOID *Data,
+ IN UINTN DataSize
+ )
+{
+ EFI_SIGNATURE_LIST *SigList;
+ UINTN SigDataSize;
+ UINT32 Index;
+ UINT32 SigCount;
+ BOOLEAN IsPk;
+ VOID *RsaContext;
+ EFI_SIGNATURE_DATA *CertData;
+ UINTN CertLen;
+
+ if (DataSize == 0) {
+ return EFI_SUCCESS;
+ }
+
+ ASSERT (VariableName != NULL && VendorGuid != NULL && Data != NULL);
+
+ if (CompareGuid (VendorGuid, &gEfiGlobalVariableGuid) && (StrCmp (VariableName, EFI_PLATFORM_KEY_NAME) == 0)){
+ IsPk = TRUE;
+ } else if ((CompareGuid (VendorGuid, &gEfiGlobalVariableGuid) && (StrCmp (VariableName, EFI_KEY_EXCHANGE_KEY_NAME) == 0)) ||
+ (CompareGuid (VendorGuid, &gEfiImageSecurityDatabaseGuid) &&
+ ((StrCmp (VariableName, EFI_IMAGE_SECURITY_DATABASE) == 0) || (StrCmp (VariableName, EFI_IMAGE_SECURITY_DATABASE1) == 0) ||
+ (StrCmp (VariableName, EFI_IMAGE_SECURITY_DATABASE2) == 0)))) {
+ IsPk = FALSE;
+ } else {
+ return EFI_SUCCESS;
+ }
+
+ SigCount = 0;
+ SigList = (EFI_SIGNATURE_LIST *) Data;
+ SigDataSize = DataSize;
+ RsaContext = NULL;
+
+ //
+ // Walk through the input signature list and check the data format.
+ // If any signature is incorrectly formed, the whole check will fail.
+ //
+ while ((SigDataSize > 0) && (SigDataSize >= SigList->SignatureListSize)) {
+ for (Index = 0; Index < (sizeof (mSupportSigItem) / sizeof (EFI_SIGNATURE_ITEM)); Index++ ) {
+ if (CompareGuid (&SigList->SignatureType, &mSupportSigItem[Index].SigType)) {
+ //
+ // The value of SignatureSize should always be 16 (size of SignatureOwner
+ // component) add the data length according to signature type.
+ //
+ if (mSupportSigItem[Index].SigDataSize != ((UINT32) ~0) &&
+ (SigList->SignatureSize - sizeof (EFI_GUID)) != mSupportSigItem[Index].SigDataSize) {
+ return EFI_INVALID_PARAMETER;
+ }
+ if (mSupportSigItem[Index].SigHeaderSize != ((UINT32) ~0) &&
+ SigList->SignatureHeaderSize != mSupportSigItem[Index].SigHeaderSize) {
+ return EFI_INVALID_PARAMETER;
+ }
+ break;
+ }
+ }
+
+ if (Index == (sizeof (mSupportSigItem) / sizeof (EFI_SIGNATURE_ITEM))) {
+ //
+ // Undefined signature type.
+ //
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (CompareGuid (&SigList->SignatureType, &gEfiCertX509Guid)) {
+ //
+ // Try to retrieve the RSA public key from the X.509 certificate.
+ // If this operation fails, it's not a valid certificate.
+ //
+ RsaContext = RsaNew ();
+ if (RsaContext == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+ CertData = (EFI_SIGNATURE_DATA *) ((UINT8 *) SigList + sizeof (EFI_SIGNATURE_LIST) + SigList->SignatureHeaderSize);
+ CertLen = SigList->SignatureSize - sizeof (EFI_GUID);
+ if (!RsaGetPublicKeyFromX509 (CertData->SignatureData, CertLen, &RsaContext)) {
+ RsaFree (RsaContext);
+ return EFI_INVALID_PARAMETER;
+ }
+ RsaFree (RsaContext);
+ }
+
+ if ((SigList->SignatureListSize - sizeof (EFI_SIGNATURE_LIST) - SigList->SignatureHeaderSize) % SigList->SignatureSize != 0) {
+ return EFI_INVALID_PARAMETER;
+ }
+ SigCount += (SigList->SignatureListSize - sizeof (EFI_SIGNATURE_LIST) - SigList->SignatureHeaderSize) / SigList->SignatureSize;
+
+ SigDataSize -= SigList->SignatureListSize;
+ SigList = (EFI_SIGNATURE_LIST *) ((UINT8 *) SigList + SigList->SignatureListSize);
+ }
+
+ if (((UINTN) SigList - (UINTN) Data) != DataSize) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (IsPk && SigCount > 1) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Update "VendorKeys" variable to record the out of band secure boot key modification.
+
+ @return EFI_SUCCESS Variable is updated successfully.
+ @return Others Failed to update variable.
+
+**/
+EFI_STATUS
+VendorKeyIsModified (
+ VOID
+ )
+{
+ EFI_STATUS Status;
+
+ if (mVendorKeyState == VENDOR_KEYS_MODIFIED) {
+ return EFI_SUCCESS;
+ }
+ mVendorKeyState = VENDOR_KEYS_MODIFIED;
+
+ Status = AuthServiceInternalUpdateVariable (
+ EFI_VENDOR_KEYS_NV_VARIABLE_NAME,
+ &gEfiVendorKeysNvGuid,
+ &mVendorKeyState,
+ sizeof (UINT8),
+ EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ return AuthServiceInternalUpdateVariable (
+ EFI_VENDOR_KEYS_VARIABLE_NAME,
+ &gEfiGlobalVariableGuid,
+ &mVendorKeyState,
+ sizeof (UINT8),
+ EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_BOOTSERVICE_ACCESS
+ );
+}
+
+/**
+ Process variable with platform key for verification.
+
+ Caution: This function may receive untrusted input.
+ This function may be invoked in SMM mode, and datasize and data are external input.
+ This function will do basic validation, before parse the data.
+ This function will parse the authentication carefully to avoid security issues, like
+ buffer overflow, integer overflow.
+ This function will check attribute carefully to avoid authentication bypass.
+
+ @param[in] VariableName Name of Variable to be found.
+ @param[in] VendorGuid Variable vendor GUID.
+ @param[in] Data Data pointer.
+ @param[in] DataSize Size of Data found. If size is less than the
+ data, this value contains the required size.
+ @param[in] Attributes Attribute value of the variable
+ @param[in] IsPk Indicate whether it is to process pk.
+
+ @return EFI_INVALID_PARAMETER Invalid parameter.
+ @return EFI_SECURITY_VIOLATION The variable does NOT pass the validation.
+ check carried out by the firmware.
+ @return EFI_SUCCESS Variable passed validation successfully.
+
+**/
+EFI_STATUS
+ProcessVarWithPk (
+ IN CHAR16 *VariableName,
+ IN EFI_GUID *VendorGuid,
+ IN VOID *Data,
+ IN UINTN DataSize,
+ IN UINT32 Attributes OPTIONAL,
+ IN BOOLEAN IsPk
+ )
+{
+ EFI_STATUS Status;
+ BOOLEAN Del;
+ UINT8 *Payload;
+ UINTN PayloadSize;
+
+ if ((Attributes & EFI_VARIABLE_NON_VOLATILE) == 0 ||
+ (Attributes & EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS) == 0) {
+ //
+ // PK, KEK and db/dbx/dbt should set EFI_VARIABLE_NON_VOLATILE attribute and should be a time-based
+ // authenticated variable.
+ //
+ return EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // Init state of Del. State may change due to secure check
+ //
+ Del = FALSE;
+ if ((InCustomMode() && UserPhysicalPresent()) || (mPlatformMode == SETUP_MODE && !IsPk)) {
+ Payload = (UINT8 *) Data + AUTHINFO2_SIZE (Data);
+ PayloadSize = DataSize - AUTHINFO2_SIZE (Data);
+ if (PayloadSize == 0) {
+ Del = TRUE;
+ }
+
+ Status = CheckSignatureListFormat(VariableName, VendorGuid, Payload, PayloadSize);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ Status = AuthServiceInternalUpdateVariableWithTimeStamp (
+ VariableName,
+ VendorGuid,
+ Payload,
+ PayloadSize,
+ Attributes,
+ &((EFI_VARIABLE_AUTHENTICATION_2 *) Data)->TimeStamp
+ );
+ if (EFI_ERROR(Status)) {
+ return Status;
+ }
+
+ if ((mPlatformMode != SETUP_MODE) || IsPk) {
+ Status = VendorKeyIsModified ();
+ }
+ } else if (mPlatformMode == USER_MODE) {
+ //
+ // Verify against X509 Cert in PK database.
+ //
+ Status = VerifyTimeBasedPayloadAndUpdate (
+ VariableName,
+ VendorGuid,
+ Data,
+ DataSize,
+ Attributes,
+ AuthVarTypePk,
+ &Del
+ );
+ } else {
+ //
+ // Verify against the certificate in data payload.
+ //
+ Status = VerifyTimeBasedPayloadAndUpdate (
+ VariableName,
+ VendorGuid,
+ Data,
+ DataSize,
+ Attributes,
+ AuthVarTypePayload,
+ &Del
+ );
+ }
+
+ if (!EFI_ERROR(Status) && IsPk) {
+ if (mPlatformMode == SETUP_MODE && !Del) {
+ //
+ // If enroll PK in setup mode, need change to user mode.
+ //
+ Status = UpdatePlatformMode (USER_MODE);
+ } else if (mPlatformMode == USER_MODE && Del){
+ //
+ // If delete PK in user mode, need change to setup mode.
+ //
+ Status = UpdatePlatformMode (SETUP_MODE);
+ }
+ }
+
+ return Status;
+}
+
+/**
+ Process variable with key exchange key for verification.
+
+ Caution: This function may receive untrusted input.
+ This function may be invoked in SMM mode, and datasize and data are external input.
+ This function will do basic validation, before parse the data.
+ This function will parse the authentication carefully to avoid security issues, like
+ buffer overflow, integer overflow.
+ This function will check attribute carefully to avoid authentication bypass.
+
+ @param[in] VariableName Name of Variable to be found.
+ @param[in] VendorGuid Variable vendor GUID.
+ @param[in] Data Data pointer.
+ @param[in] DataSize Size of Data found. If size is less than the
+ data, this value contains the required size.
+ @param[in] Attributes Attribute value of the variable.
+
+ @return EFI_INVALID_PARAMETER Invalid parameter.
+ @return EFI_SECURITY_VIOLATION The variable does NOT pass the validation
+ check carried out by the firmware.
+ @return EFI_SUCCESS Variable pass validation successfully.
+
+**/
+EFI_STATUS
+ProcessVarWithKek (
+ IN CHAR16 *VariableName,
+ IN EFI_GUID *VendorGuid,
+ IN VOID *Data,
+ IN UINTN DataSize,
+ IN UINT32 Attributes OPTIONAL
+ )
+{
+ EFI_STATUS Status;
+ UINT8 *Payload;
+ UINTN PayloadSize;
+
+ if ((Attributes & EFI_VARIABLE_NON_VOLATILE) == 0 ||
+ (Attributes & EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS) == 0) {
+ //
+ // DB, DBX and DBT should set EFI_VARIABLE_NON_VOLATILE attribute and should be a time-based
+ // authenticated variable.
+ //
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Status = EFI_SUCCESS;
+ if (mPlatformMode == USER_MODE && !(InCustomMode() && UserPhysicalPresent())) {
+ //
+ // Time-based, verify against X509 Cert KEK.
+ //
+ return VerifyTimeBasedPayloadAndUpdate (
+ VariableName,
+ VendorGuid,
+ Data,
+ DataSize,
+ Attributes,
+ AuthVarTypeKek,
+ NULL
+ );
+ } else {
+ //
+ // If in setup mode or custom secure boot mode, no authentication needed.
+ //
+ Payload = (UINT8 *) Data + AUTHINFO2_SIZE (Data);
+ PayloadSize = DataSize - AUTHINFO2_SIZE (Data);
+
+ Status = CheckSignatureListFormat(VariableName, VendorGuid, Payload, PayloadSize);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ Status = AuthServiceInternalUpdateVariableWithTimeStamp (
+ VariableName,
+ VendorGuid,
+ Payload,
+ PayloadSize,
+ Attributes,
+ &((EFI_VARIABLE_AUTHENTICATION_2 *) Data)->TimeStamp
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ if (mPlatformMode != SETUP_MODE) {
+ Status = VendorKeyIsModified ();
+ }
+ }
+
+ return Status;
+}
+
+/**
+ Check if it is to delete auth variable.
+
+ @param[in] OrgAttributes Original attribute value of the variable.
+ @param[in] Data Data pointer.
+ @param[in] DataSize Size of Data.
+ @param[in] Attributes Attribute value of the variable.
+
+ @retval TRUE It is to delete auth variable.
+ @retval FALSE It is not to delete auth variable.
+
+**/
+BOOLEAN
+IsDeleteAuthVariable (
+ IN UINT32 OrgAttributes,
+ IN VOID *Data,
+ IN UINTN DataSize,
+ IN UINT32 Attributes
+ )
+{
+ BOOLEAN Del;
+ UINTN PayloadSize;
+
+ Del = FALSE;
+
+ //
+ // To delete a variable created with the EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS
+ // or the EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS attribute,
+ // SetVariable must be used with attributes matching the existing variable
+ // and the DataSize set to the size of the AuthInfo descriptor.
+ //
+ if ((Attributes == OrgAttributes) &&
+ ((Attributes & (EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS | EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS)) != 0)) {
+ if ((Attributes & EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS) != 0) {
+ PayloadSize = DataSize - AUTHINFO2_SIZE (Data);
+ if (PayloadSize == 0) {
+ Del = TRUE;
+ }
+ } else {
+ PayloadSize = DataSize - AUTHINFO_SIZE;
+ if (PayloadSize == 0) {
+ Del = TRUE;
+ }
+ }
+ }
+
+ return Del;
+}
+
+/**
+ Process variable with EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS set
+
+ Caution: This function may receive untrusted input.
+ This function may be invoked in SMM mode, and datasize and data are external input.
+ This function will do basic validation, before parse the data.
+ This function will parse the authentication carefully to avoid security issues, like
+ buffer overflow, integer overflow.
+ This function will check attribute carefully to avoid authentication bypass.
+
+ @param[in] VariableName Name of the variable.
+ @param[in] VendorGuid Variable vendor GUID.
+ @param[in] Data Data pointer.
+ @param[in] DataSize Size of Data.
+ @param[in] Attributes Attribute value of the variable.
+
+ @return EFI_INVALID_PARAMETER Invalid parameter.
+ @return EFI_WRITE_PROTECTED Variable is write-protected and needs authentication with
+ EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS or EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS set.
+ @return EFI_OUT_OF_RESOURCES The Database to save the public key is full.
+ @return EFI_SECURITY_VIOLATION The variable is with EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS
+ set, but the AuthInfo does NOT pass the validation
+ check carried out by the firmware.
+ @return EFI_SUCCESS Variable is not write-protected or pass validation successfully.
+
+**/
+EFI_STATUS
+ProcessVariable (
+ IN CHAR16 *VariableName,
+ IN EFI_GUID *VendorGuid,
+ IN VOID *Data,
+ IN UINTN DataSize,
+ IN UINT32 Attributes
+ )
+{
+ EFI_STATUS Status;
+ AUTH_VARIABLE_INFO OrgVariableInfo;
+
+ Status = EFI_SUCCESS;
+
+ ZeroMem (&OrgVariableInfo, sizeof (OrgVariableInfo));
+ Status = mAuthVarLibContextIn->FindVariable (
+ VariableName,
+ VendorGuid,
+ &OrgVariableInfo
+ );
+
+ // If the VariablePolicy engine is disabled, allow deletion of any authenticated variables.
+ if ((!EFI_ERROR (Status)) && IsDeleteAuthVariable (OrgVariableInfo.Attributes, Data, DataSize, Attributes) && (UserPhysicalPresent() || !IsVariablePolicyEnabled())) {
+ //
+ // Allow the delete operation of common authenticated variable(AT or AW) at user physical presence.
+ //
+ Status = AuthServiceInternalUpdateVariable (
+ VariableName,
+ VendorGuid,
+ NULL,
+ 0,
+ 0
+ );
+ if (!EFI_ERROR (Status) && ((Attributes & EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS) != 0)) {
+ Status = DeleteCertsFromDb (VariableName, VendorGuid, Attributes);
+ }
+
+ return Status;
+ }
+
+ if (NeedPhysicallyPresent (VariableName, VendorGuid) && !UserPhysicalPresent()) {
+ //
+ // This variable is protected, only physical present user could modify its value.
+ //
+ return EFI_SECURITY_VIOLATION;
+ }
+
+ //
+ if ((Attributes & EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS) != 0) {
+ //
+ // Reject Counter Based Auth Variable processing request.
+ //
+ return EFI_UNSUPPORTED;
+ } else if ((Attributes & EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS) != 0) {
+ //
+ // Process Time-based Authenticated variable.
+ //
+ return VerifyTimeBasedPayloadAndUpdate (
+ VariableName,
+ VendorGuid,
+ Data,
+ DataSize,
+ Attributes,
+ AuthVarTypePriv,
+ NULL
+ );
+ }
+
+ if ((OrgVariableInfo.Data != NULL) &&
+ ((OrgVariableInfo.Attributes & (EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS | EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS)) != 0)) {
+ //
+ // If the variable is already write-protected, it always needs authentication before update.
+ //
+ return EFI_WRITE_PROTECTED;
+ }
+
+ //
+ // Not authenticated variable, just update variable as usual.
+ //
+ Status = AuthServiceInternalUpdateVariable (VariableName, VendorGuid, Data, DataSize, Attributes);
+ return Status;
+
+}
+
+/**
+ Filter out the duplicated EFI_SIGNATURE_DATA from the new data by comparing to the original data.
+
+ @param[in] Data Pointer to original EFI_SIGNATURE_LIST.
+ @param[in] DataSize Size of Data buffer.
+ @param[in, out] NewData Pointer to new EFI_SIGNATURE_LIST.
+ @param[in, out] NewDataSize Size of NewData buffer.
+
+**/
+EFI_STATUS
+FilterSignatureList (
+ IN VOID *Data,
+ IN UINTN DataSize,
+ IN OUT VOID *NewData,
+ IN OUT UINTN *NewDataSize
+ )
+{
+ EFI_SIGNATURE_LIST *CertList;
+ EFI_SIGNATURE_DATA *Cert;
+ UINTN CertCount;
+ EFI_SIGNATURE_LIST *NewCertList;
+ EFI_SIGNATURE_DATA *NewCert;
+ UINTN NewCertCount;
+ UINTN Index;
+ UINTN Index2;
+ UINTN Size;
+ UINT8 *Tail;
+ UINTN CopiedCount;
+ UINTN SignatureListSize;
+ BOOLEAN IsNewCert;
+ UINT8 *TempData;
+ UINTN TempDataSize;
+ EFI_STATUS Status;
+
+ if (*NewDataSize == 0) {
+ return EFI_SUCCESS;
+ }
+
+ TempDataSize = *NewDataSize;
+ Status = mAuthVarLibContextIn->GetScratchBuffer (&TempDataSize, (VOID **) &TempData);
+ if (EFI_ERROR (Status)) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ Tail = TempData;
+
+ NewCertList = (EFI_SIGNATURE_LIST *) NewData;
+ while ((*NewDataSize > 0) && (*NewDataSize >= NewCertList->SignatureListSize)) {
+ NewCert = (EFI_SIGNATURE_DATA *) ((UINT8 *) NewCertList + sizeof (EFI_SIGNATURE_LIST) + NewCertList->SignatureHeaderSize);
+ NewCertCount = (NewCertList->SignatureListSize - sizeof (EFI_SIGNATURE_LIST) - NewCertList->SignatureHeaderSize) / NewCertList->SignatureSize;
+
+ CopiedCount = 0;
+ for (Index = 0; Index < NewCertCount; Index++) {
+ IsNewCert = TRUE;
+
+ Size = DataSize;
+ CertList = (EFI_SIGNATURE_LIST *) Data;
+ while ((Size > 0) && (Size >= CertList->SignatureListSize)) {
+ if (CompareGuid (&CertList->SignatureType, &NewCertList->SignatureType) &&
+ (CertList->SignatureSize == NewCertList->SignatureSize)) {
+ Cert = (EFI_SIGNATURE_DATA *) ((UINT8 *) CertList + sizeof (EFI_SIGNATURE_LIST) + CertList->SignatureHeaderSize);
+ CertCount = (CertList->SignatureListSize - sizeof (EFI_SIGNATURE_LIST) - CertList->SignatureHeaderSize) / CertList->SignatureSize;
+ for (Index2 = 0; Index2 < CertCount; Index2++) {
+ //
+ // Iterate each Signature Data in this Signature List.
+ //
+ if (CompareMem (NewCert, Cert, CertList->SignatureSize) == 0) {
+ IsNewCert = FALSE;
+ break;
+ }
+ Cert = (EFI_SIGNATURE_DATA *) ((UINT8 *) Cert + CertList->SignatureSize);
+ }
+ }
+
+ if (!IsNewCert) {
+ break;
+ }
+ Size -= CertList->SignatureListSize;
+ CertList = (EFI_SIGNATURE_LIST *) ((UINT8 *) CertList + CertList->SignatureListSize);
+ }
+
+ if (IsNewCert) {
+ //
+ // New EFI_SIGNATURE_DATA, keep it.
+ //
+ if (CopiedCount == 0) {
+ //
+ // Copy EFI_SIGNATURE_LIST header for only once.
+ //
+ CopyMem (Tail, NewCertList, sizeof (EFI_SIGNATURE_LIST) + NewCertList->SignatureHeaderSize);
+ Tail = Tail + sizeof (EFI_SIGNATURE_LIST) + NewCertList->SignatureHeaderSize;
+ }
+
+ CopyMem (Tail, NewCert, NewCertList->SignatureSize);
+ Tail += NewCertList->SignatureSize;
+ CopiedCount++;
+ }
+
+ NewCert = (EFI_SIGNATURE_DATA *) ((UINT8 *) NewCert + NewCertList->SignatureSize);
+ }
+
+ //
+ // Update SignatureListSize in the kept EFI_SIGNATURE_LIST.
+ //
+ if (CopiedCount != 0) {
+ SignatureListSize = sizeof (EFI_SIGNATURE_LIST) + NewCertList->SignatureHeaderSize + (CopiedCount * NewCertList->SignatureSize);
+ CertList = (EFI_SIGNATURE_LIST *) (Tail - SignatureListSize);
+ CertList->SignatureListSize = (UINT32) SignatureListSize;
+ }
+
+ *NewDataSize -= NewCertList->SignatureListSize;
+ NewCertList = (EFI_SIGNATURE_LIST *) ((UINT8 *) NewCertList + NewCertList->SignatureListSize);
+ }
+
+ TempDataSize = (Tail - (UINT8 *) TempData);
+
+ CopyMem (NewData, TempData, TempDataSize);
+ *NewDataSize = TempDataSize;
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Compare two EFI_TIME data.
+
+
+ @param FirstTime A pointer to the first EFI_TIME data.
+ @param SecondTime A pointer to the second EFI_TIME data.
+
+ @retval TRUE The FirstTime is not later than the SecondTime.
+ @retval FALSE The FirstTime is later than the SecondTime.
+
+**/
+BOOLEAN
+AuthServiceInternalCompareTimeStamp (
+ IN EFI_TIME *FirstTime,
+ IN EFI_TIME *SecondTime
+ )
+{
+ if (FirstTime->Year != SecondTime->Year) {
+ return (BOOLEAN) (FirstTime->Year < SecondTime->Year);
+ } else if (FirstTime->Month != SecondTime->Month) {
+ return (BOOLEAN) (FirstTime->Month < SecondTime->Month);
+ } else if (FirstTime->Day != SecondTime->Day) {
+ return (BOOLEAN) (FirstTime->Day < SecondTime->Day);
+ } else if (FirstTime->Hour != SecondTime->Hour) {
+ return (BOOLEAN) (FirstTime->Hour < SecondTime->Hour);
+ } else if (FirstTime->Minute != SecondTime->Minute) {
+ return (BOOLEAN) (FirstTime->Minute < SecondTime->Minute);
+ }
+
+ return (BOOLEAN) (FirstTime->Second <= SecondTime->Second);
+}
+
+/**
+ Calculate SHA256 digest of SignerCert CommonName + ToplevelCert tbsCertificate
+ SignerCert and ToplevelCert are inside the signer certificate chain.
+
+ @param[in] SignerCert A pointer to SignerCert data.
+ @param[in] SignerCertSize Length of SignerCert data.
+ @param[in] TopLevelCert A pointer to TopLevelCert data.
+ @param[in] TopLevelCertSize Length of TopLevelCert data.
+ @param[out] Sha256Digest Sha256 digest calculated.
+
+ @return EFI_ABORTED Digest process failed.
+ @return EFI_SUCCESS SHA256 Digest is successfully calculated.
+
+**/
+EFI_STATUS
+CalculatePrivAuthVarSignChainSHA256Digest(
+ IN UINT8 *SignerCert,
+ IN UINTN SignerCertSize,
+ IN UINT8 *TopLevelCert,
+ IN UINTN TopLevelCertSize,
+ OUT UINT8 *Sha256Digest
+ )
+{
+ UINT8 *TbsCert;
+ UINTN TbsCertSize;
+ CHAR8 CertCommonName[128];
+ UINTN CertCommonNameSize;
+ BOOLEAN CryptoStatus;
+ EFI_STATUS Status;
+
+ CertCommonNameSize = sizeof(CertCommonName);
+
+ //
+ // Get SignerCert CommonName
+ //
+ Status = X509GetCommonName(SignerCert, SignerCertSize, CertCommonName, &CertCommonNameSize);
+ if (EFI_ERROR(Status)) {
+ DEBUG((DEBUG_INFO, "%a Get SignerCert CommonName failed with status %x\n", __FUNCTION__, Status));
+ return EFI_ABORTED;
+ }
+
+ //
+ // Get TopLevelCert tbsCertificate
+ //
+ if (!X509GetTBSCert(TopLevelCert, TopLevelCertSize, &TbsCert, &TbsCertSize)) {
+ DEBUG((DEBUG_INFO, "%a Get Top-level Cert tbsCertificate failed!\n", __FUNCTION__));
+ return EFI_ABORTED;
+ }
+
+ //
+ // Digest SignerCert CN + TopLevelCert tbsCertificate
+ //
+ ZeroMem (Sha256Digest, SHA256_DIGEST_SIZE);
+ CryptoStatus = Sha256Init (mHashCtx);
+ if (!CryptoStatus) {
+ return EFI_ABORTED;
+ }
+
+ //
+ // '\0' is forced in CertCommonName. No overflow issue
+ //
+ CryptoStatus = Sha256Update (
+ mHashCtx,
+ CertCommonName,
+ AsciiStrLen (CertCommonName)
+ );
+ if (!CryptoStatus) {
+ return EFI_ABORTED;
+ }
+
+ CryptoStatus = Sha256Update (mHashCtx, TbsCert, TbsCertSize);
+ if (!CryptoStatus) {
+ return EFI_ABORTED;
+ }
+
+ CryptoStatus = Sha256Final (mHashCtx, Sha256Digest);
+ if (!CryptoStatus) {
+ return EFI_ABORTED;
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Find matching signer's certificates for common authenticated variable
+ by corresponding VariableName and VendorGuid from "certdb" or "certdbv".
+
+ The data format of "certdb" or "certdbv":
+ //
+ // UINT32 CertDbListSize;
+ // /// AUTH_CERT_DB_DATA Certs1[];
+ // /// AUTH_CERT_DB_DATA Certs2[];
+ // /// ...
+ // /// AUTH_CERT_DB_DATA Certsn[];
+ //
+
+ @param[in] VariableName Name of authenticated Variable.
+ @param[in] VendorGuid Vendor GUID of authenticated Variable.
+ @param[in] Data Pointer to variable "certdb" or "certdbv".
+ @param[in] DataSize Size of variable "certdb" or "certdbv".
+ @param[out] CertOffset Offset of matching CertData, from starting of Data.
+ @param[out] CertDataSize Length of CertData in bytes.
+ @param[out] CertNodeOffset Offset of matching AUTH_CERT_DB_DATA , from
+ starting of Data.
+ @param[out] CertNodeSize Length of AUTH_CERT_DB_DATA in bytes.
+
+ @retval EFI_INVALID_PARAMETER Any input parameter is invalid.
+ @retval EFI_NOT_FOUND Fail to find matching certs.
+ @retval EFI_SUCCESS Find matching certs and output parameters.
+
+**/
+EFI_STATUS
+FindCertsFromDb (
+ IN CHAR16 *VariableName,
+ IN EFI_GUID *VendorGuid,
+ IN UINT8 *Data,
+ IN UINTN DataSize,
+ OUT UINT32 *CertOffset, OPTIONAL
+ OUT UINT32 *CertDataSize, OPTIONAL
+ OUT UINT32 *CertNodeOffset,OPTIONAL
+ OUT UINT32 *CertNodeSize OPTIONAL
+ )
+{
+ UINT32 Offset;
+ AUTH_CERT_DB_DATA *Ptr;
+ UINT32 CertSize;
+ UINT32 NameSize;
+ UINT32 NodeSize;
+ UINT32 CertDbListSize;
+
+ if ((VariableName == NULL) || (VendorGuid == NULL) || (Data == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // Check whether DataSize matches recorded CertDbListSize.
+ //
+ if (DataSize < sizeof (UINT32)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ CertDbListSize = ReadUnaligned32 ((UINT32 *) Data);
+
+ if (CertDbListSize != (UINT32) DataSize) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Offset = sizeof (UINT32);
+
+ //
+ // Get corresponding certificates by VendorGuid and VariableName.
+ //
+ while (Offset < (UINT32) DataSize) {
+ Ptr = (AUTH_CERT_DB_DATA *) (Data + Offset);
+ //
+ // Check whether VendorGuid matches.
+ //
+ if (CompareGuid (&Ptr->VendorGuid, VendorGuid)) {
+ NodeSize = ReadUnaligned32 (&Ptr->CertNodeSize);
+ NameSize = ReadUnaligned32 (&Ptr->NameSize);
+ CertSize = ReadUnaligned32 (&Ptr->CertDataSize);
+
+ if (NodeSize != sizeof (EFI_GUID) + sizeof (UINT32) * 3 + CertSize +
+ sizeof (CHAR16) * NameSize) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Offset = Offset + sizeof (EFI_GUID) + sizeof (UINT32) * 3;
+ //
+ // Check whether VariableName matches.
+ //
+ if ((NameSize == StrLen (VariableName)) &&
+ (CompareMem (Data + Offset, VariableName, NameSize * sizeof (CHAR16)) == 0)) {
+ Offset = Offset + NameSize * sizeof (CHAR16);
+
+ if (CertOffset != NULL) {
+ *CertOffset = Offset;
+ }
+
+ if (CertDataSize != NULL) {
+ *CertDataSize = CertSize;
+ }
+
+ if (CertNodeOffset != NULL) {
+ *CertNodeOffset = (UINT32) ((UINT8 *) Ptr - Data);
+ }
+
+ if (CertNodeSize != NULL) {
+ *CertNodeSize = NodeSize;
+ }
+
+ return EFI_SUCCESS;
+ } else {
+ Offset = Offset + NameSize * sizeof (CHAR16) + CertSize;
+ }
+ } else {
+ NodeSize = ReadUnaligned32 (&Ptr->CertNodeSize);
+ Offset = Offset + NodeSize;
+ }
+ }
+
+ return EFI_NOT_FOUND;
+}
+
+/**
+ Retrieve signer's certificates for common authenticated variable
+ by corresponding VariableName and VendorGuid from "certdb"
+ or "certdbv" according to authenticated variable attributes.
+
+ @param[in] VariableName Name of authenticated Variable.
+ @param[in] VendorGuid Vendor GUID of authenticated Variable.
+ @param[in] Attributes Attributes of authenticated variable.
+ @param[out] CertData Pointer to signer's certificates.
+ @param[out] CertDataSize Length of CertData in bytes.
+
+ @retval EFI_INVALID_PARAMETER Any input parameter is invalid.
+ @retval EFI_NOT_FOUND Fail to find "certdb"/"certdbv" or matching certs.
+ @retval EFI_SUCCESS Get signer's certificates successfully.
+
+**/
+EFI_STATUS
+GetCertsFromDb (
+ IN CHAR16 *VariableName,
+ IN EFI_GUID *VendorGuid,
+ IN UINT32 Attributes,
+ OUT UINT8 **CertData,
+ OUT UINT32 *CertDataSize
+ )
+{
+ EFI_STATUS Status;
+ UINT8 *Data;
+ UINTN DataSize;
+ UINT32 CertOffset;
+ CHAR16 *DbName;
+
+ if ((VariableName == NULL) || (VendorGuid == NULL) || (CertData == NULL) || (CertDataSize == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+
+ if ((Attributes & EFI_VARIABLE_NON_VOLATILE) != 0) {
+ //
+ // Get variable "certdb".
+ //
+ DbName = EFI_CERT_DB_NAME;
+ } else {
+ //
+ // Get variable "certdbv".
+ //
+ DbName = EFI_CERT_DB_VOLATILE_NAME;
+ }
+
+ //
+ // Get variable "certdb" or "certdbv".
+ //
+ Status = AuthServiceInternalFindVariable (
+ DbName,
+ &gEfiCertDbGuid,
+ (VOID **) &Data,
+ &DataSize
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ if ((DataSize == 0) || (Data == NULL)) {
+ ASSERT (FALSE);
+ return EFI_NOT_FOUND;
+ }
+
+ Status = FindCertsFromDb (
+ VariableName,
+ VendorGuid,
+ Data,
+ DataSize,
+ &CertOffset,
+ CertDataSize,
+ NULL,
+ NULL
+ );
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ *CertData = Data + CertOffset;
+ return EFI_SUCCESS;
+}
+
+/**
+ Delete matching signer's certificates when deleting common authenticated
+ variable by corresponding VariableName and VendorGuid from "certdb" or
+ "certdbv" according to authenticated variable attributes.
+
+ @param[in] VariableName Name of authenticated Variable.
+ @param[in] VendorGuid Vendor GUID of authenticated Variable.
+ @param[in] Attributes Attributes of authenticated variable.
+
+ @retval EFI_INVALID_PARAMETER Any input parameter is invalid.
+ @retval EFI_NOT_FOUND Fail to find "certdb"/"certdbv" or matching certs.
+ @retval EFI_OUT_OF_RESOURCES The operation is failed due to lack of resources.
+ @retval EFI_SUCCESS The operation is completed successfully.
+
+**/
+EFI_STATUS
+DeleteCertsFromDb (
+ IN CHAR16 *VariableName,
+ IN EFI_GUID *VendorGuid,
+ IN UINT32 Attributes
+ )
+{
+ EFI_STATUS Status;
+ UINT8 *Data;
+ UINTN DataSize;
+ UINT32 VarAttr;
+ UINT32 CertNodeOffset;
+ UINT32 CertNodeSize;
+ UINT8 *NewCertDb;
+ UINT32 NewCertDbSize;
+ CHAR16 *DbName;
+
+ if ((VariableName == NULL) || (VendorGuid == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if ((Attributes & EFI_VARIABLE_NON_VOLATILE) != 0) {
+ //
+ // Get variable "certdb".
+ //
+ DbName = EFI_CERT_DB_NAME;
+ VarAttr = EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS;
+ } else {
+ //
+ // Get variable "certdbv".
+ //
+ DbName = EFI_CERT_DB_VOLATILE_NAME;
+ VarAttr = EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS;
+ }
+
+ Status = AuthServiceInternalFindVariable (
+ DbName,
+ &gEfiCertDbGuid,
+ (VOID **) &Data,
+ &DataSize
+ );
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ if ((DataSize == 0) || (Data == NULL)) {
+ ASSERT (FALSE);
+ return EFI_NOT_FOUND;
+ }
+
+ if (DataSize == sizeof (UINT32)) {
+ //
+ // There is no certs in "certdb" or "certdbv".
+ //
+ return EFI_SUCCESS;
+ }
+
+ //
+ // Get corresponding cert node from "certdb" or "certdbv".
+ //
+ Status = FindCertsFromDb (
+ VariableName,
+ VendorGuid,
+ Data,
+ DataSize,
+ NULL,
+ NULL,
+ &CertNodeOffset,
+ &CertNodeSize
+ );
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ if (DataSize < (CertNodeOffset + CertNodeSize)) {
+ return EFI_NOT_FOUND;
+ }
+
+ //
+ // Construct new data content of variable "certdb" or "certdbv".
+ //
+ NewCertDbSize = (UINT32) DataSize - CertNodeSize;
+ NewCertDb = (UINT8*) mCertDbStore;
+
+ //
+ // Copy the DB entries before deleting node.
+ //
+ CopyMem (NewCertDb, Data, CertNodeOffset);
+ //
+ // Update CertDbListSize.
+ //
+ CopyMem (NewCertDb, &NewCertDbSize, sizeof (UINT32));
+ //
+ // Copy the DB entries after deleting node.
+ //
+ if (DataSize > (CertNodeOffset + CertNodeSize)) {
+ CopyMem (
+ NewCertDb + CertNodeOffset,
+ Data + CertNodeOffset + CertNodeSize,
+ DataSize - CertNodeOffset - CertNodeSize
+ );
+ }
+
+ //
+ // Set "certdb" or "certdbv".
+ //
+ Status = AuthServiceInternalUpdateVariable (
+ DbName,
+ &gEfiCertDbGuid,
+ NewCertDb,
+ NewCertDbSize,
+ VarAttr
+ );
+
+ return Status;
+}
+
+/**
+ Insert signer's certificates for common authenticated variable with VariableName
+ and VendorGuid in AUTH_CERT_DB_DATA to "certdb" or "certdbv" according to
+ time based authenticated variable attributes. CertData is the SHA256 digest of
+ SignerCert CommonName + TopLevelCert tbsCertificate.
+
+ @param[in] VariableName Name of authenticated Variable.
+ @param[in] VendorGuid Vendor GUID of authenticated Variable.
+ @param[in] Attributes Attributes of authenticated variable.
+ @param[in] SignerCert Signer certificate data.
+ @param[in] SignerCertSize Length of signer certificate.
+ @param[in] TopLevelCert Top-level certificate data.
+ @param[in] TopLevelCertSize Length of top-level certificate.
+
+ @retval EFI_INVALID_PARAMETER Any input parameter is invalid.
+ @retval EFI_ACCESS_DENIED An AUTH_CERT_DB_DATA entry with same VariableName
+ and VendorGuid already exists.
+ @retval EFI_OUT_OF_RESOURCES The operation is failed due to lack of resources.
+ @retval EFI_SUCCESS Insert an AUTH_CERT_DB_DATA entry to "certdb" or "certdbv"
+
+**/
+EFI_STATUS
+InsertCertsToDb (
+ IN CHAR16 *VariableName,
+ IN EFI_GUID *VendorGuid,
+ IN UINT32 Attributes,
+ IN UINT8 *SignerCert,
+ IN UINTN SignerCertSize,
+ IN UINT8 *TopLevelCert,
+ IN UINTN TopLevelCertSize
+ )
+{
+ EFI_STATUS Status;
+ UINT8 *Data;
+ UINTN DataSize;
+ UINT32 VarAttr;
+ UINT8 *NewCertDb;
+ UINT32 NewCertDbSize;
+ UINT32 CertNodeSize;
+ UINT32 NameSize;
+ UINT32 CertDataSize;
+ AUTH_CERT_DB_DATA *Ptr;
+ CHAR16 *DbName;
+ UINT8 Sha256Digest[SHA256_DIGEST_SIZE];
+
+ if ((VariableName == NULL) || (VendorGuid == NULL) || (SignerCert == NULL) ||(TopLevelCert == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if ((Attributes & EFI_VARIABLE_NON_VOLATILE) != 0) {
+ //
+ // Get variable "certdb".
+ //
+ DbName = EFI_CERT_DB_NAME;
+ VarAttr = EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS;
+ } else {
+ //
+ // Get variable "certdbv".
+ //
+ DbName = EFI_CERT_DB_VOLATILE_NAME;
+ VarAttr = EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS;
+ }
+
+ //
+ // Get variable "certdb" or "certdbv".
+ //
+ Status = AuthServiceInternalFindVariable (
+ DbName,
+ &gEfiCertDbGuid,
+ (VOID **) &Data,
+ &DataSize
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ if ((DataSize == 0) || (Data == NULL)) {
+ ASSERT (FALSE);
+ return EFI_NOT_FOUND;
+ }
+
+ //
+ // Find whether matching cert node already exists in "certdb" or "certdbv".
+ // If yes return error.
+ //
+ Status = FindCertsFromDb (
+ VariableName,
+ VendorGuid,
+ Data,
+ DataSize,
+ NULL,
+ NULL,
+ NULL,
+ NULL
+ );
+
+ if (!EFI_ERROR (Status)) {
+ ASSERT (FALSE);
+ return EFI_ACCESS_DENIED;
+ }
+
+ //
+ // Construct new data content of variable "certdb" or "certdbv".
+ //
+ NameSize = (UINT32) StrLen (VariableName);
+ CertDataSize = sizeof(Sha256Digest);
+ CertNodeSize = sizeof (AUTH_CERT_DB_DATA) + (UINT32) CertDataSize + NameSize * sizeof (CHAR16);
+ NewCertDbSize = (UINT32) DataSize + CertNodeSize;
+ if (NewCertDbSize > mMaxCertDbSize) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ Status = CalculatePrivAuthVarSignChainSHA256Digest(
+ SignerCert,
+ SignerCertSize,
+ TopLevelCert,
+ TopLevelCertSize,
+ Sha256Digest
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ NewCertDb = (UINT8*) mCertDbStore;
+
+ //
+ // Copy the DB entries before inserting node.
+ //
+ CopyMem (NewCertDb, Data, DataSize);
+ //
+ // Update CertDbListSize.
+ //
+ CopyMem (NewCertDb, &NewCertDbSize, sizeof (UINT32));
+ //
+ // Construct new cert node.
+ //
+ Ptr = (AUTH_CERT_DB_DATA *) (NewCertDb + DataSize);
+ CopyGuid (&Ptr->VendorGuid, VendorGuid);
+ CopyMem (&Ptr->CertNodeSize, &CertNodeSize, sizeof (UINT32));
+ CopyMem (&Ptr->NameSize, &NameSize, sizeof (UINT32));
+ CopyMem (&Ptr->CertDataSize, &CertDataSize, sizeof (UINT32));
+
+ CopyMem (
+ (UINT8 *) Ptr + sizeof (AUTH_CERT_DB_DATA),
+ VariableName,
+ NameSize * sizeof (CHAR16)
+ );
+
+ CopyMem (
+ (UINT8 *) Ptr + sizeof (AUTH_CERT_DB_DATA) + NameSize * sizeof (CHAR16),
+ Sha256Digest,
+ CertDataSize
+ );
+
+ //
+ // Set "certdb" or "certdbv".
+ //
+ Status = AuthServiceInternalUpdateVariable (
+ DbName,
+ &gEfiCertDbGuid,
+ NewCertDb,
+ NewCertDbSize,
+ VarAttr
+ );
+
+ return Status;
+}
+
+/**
+ Clean up signer's certificates for common authenticated variable
+ by corresponding VariableName and VendorGuid from "certdb".
+ System may break down during Timebased Variable update & certdb update,
+ make them inconsistent, this function is called in AuthVariable Init
+ to ensure consistency.
+
+ @retval EFI_NOT_FOUND Fail to find variable "certdb".
+ @retval EFI_OUT_OF_RESOURCES The operation is failed due to lack of resources.
+ @retval EFI_SUCCESS The operation is completed successfully.
+
+**/
+EFI_STATUS
+CleanCertsFromDb (
+ VOID
+ )
+{
+ UINT32 Offset;
+ AUTH_CERT_DB_DATA *Ptr;
+ UINT32 NameSize;
+ UINT32 NodeSize;
+ CHAR16 *VariableName;
+ EFI_STATUS Status;
+ BOOLEAN CertCleaned;
+ UINT8 *Data;
+ UINTN DataSize;
+ EFI_GUID AuthVarGuid;
+ AUTH_VARIABLE_INFO AuthVariableInfo;
+
+ Status = EFI_SUCCESS;
+
+ //
+ // Get corresponding certificates by VendorGuid and VariableName.
+ //
+ do {
+ CertCleaned = FALSE;
+
+ //
+ // Get latest variable "certdb"
+ //
+ Status = AuthServiceInternalFindVariable (
+ EFI_CERT_DB_NAME,
+ &gEfiCertDbGuid,
+ (VOID **) &Data,
+ &DataSize
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ if ((DataSize == 0) || (Data == NULL)) {
+ ASSERT (FALSE);
+ return EFI_NOT_FOUND;
+ }
+
+ Offset = sizeof (UINT32);
+
+ while (Offset < (UINT32) DataSize) {
+ Ptr = (AUTH_CERT_DB_DATA *) (Data + Offset);
+ NodeSize = ReadUnaligned32 (&Ptr->CertNodeSize);
+ NameSize = ReadUnaligned32 (&Ptr->NameSize);
+
+ //
+ // Get VarName tailed with '\0'
+ //
+ VariableName = AllocateZeroPool((NameSize + 1) * sizeof(CHAR16));
+ if (VariableName == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+ CopyMem (VariableName, (UINT8 *) Ptr + sizeof (AUTH_CERT_DB_DATA), NameSize * sizeof(CHAR16));
+ //
+ // Keep VarGuid aligned
+ //
+ CopyMem (&AuthVarGuid, &Ptr->VendorGuid, sizeof(EFI_GUID));
+
+ //
+ // Find corresponding time auth variable
+ //
+ ZeroMem (&AuthVariableInfo, sizeof (AuthVariableInfo));
+ Status = mAuthVarLibContextIn->FindVariable (
+ VariableName,
+ &AuthVarGuid,
+ &AuthVariableInfo
+ );
+
+ if (EFI_ERROR(Status) || (AuthVariableInfo.Attributes & EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS) == 0) {
+ //
+ // While cleaning certdb, always delete the variable in certdb regardless of it attributes.
+ //
+ Status = DeleteCertsFromDb(
+ VariableName,
+ &AuthVarGuid,
+ AuthVariableInfo.Attributes | EFI_VARIABLE_NON_VOLATILE
+ );
+ CertCleaned = TRUE;
+ DEBUG((EFI_D_INFO, "Recovery!! Cert for Auth Variable %s Guid %g is removed for consistency\n", VariableName, &AuthVarGuid));
+ FreePool(VariableName);
+ break;
+ }
+
+ FreePool(VariableName);
+ Offset = Offset + NodeSize;
+ }
+ } while (CertCleaned);
+
+ return Status;
+}
+
+/**
+ Process variable with EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS set
+
+ Caution: This function may receive untrusted input.
+ This function may be invoked in SMM mode, and datasize and data are external input.
+ This function will do basic validation, before parse the data.
+ This function will parse the authentication carefully to avoid security issues, like
+ buffer overflow, integer overflow.
+
+ @param[in] VariableName Name of Variable to be found.
+ @param[in] VendorGuid Variable vendor GUID.
+ @param[in] Data Data pointer.
+ @param[in] DataSize Size of Data found. If size is less than the
+ data, this value contains the required size.
+ @param[in] Attributes Attribute value of the variable.
+ @param[in] AuthVarType Verify against PK, KEK database, private database or certificate in data payload.
+ @param[in] OrgTimeStamp Pointer to original time stamp,
+ original variable is not found if NULL.
+ @param[out] VarPayloadPtr Pointer to variable payload address.
+ @param[out] VarPayloadSize Pointer to variable payload size.
+
+ @retval EFI_INVALID_PARAMETER Invalid parameter.
+ @retval EFI_SECURITY_VIOLATION The variable does NOT pass the validation
+ check carried out by the firmware.
+ @retval EFI_OUT_OF_RESOURCES Failed to process variable due to lack
+ of resources.
+ @retval EFI_SUCCESS Variable pass validation successfully.
+
+**/
+EFI_STATUS
+VerifyTimeBasedPayload (
+ IN CHAR16 *VariableName,
+ IN EFI_GUID *VendorGuid,
+ IN VOID *Data,
+ IN UINTN DataSize,
+ IN UINT32 Attributes,
+ IN AUTHVAR_TYPE AuthVarType,
+ IN EFI_TIME *OrgTimeStamp,
+ OUT UINT8 **VarPayloadPtr,
+ OUT UINTN *VarPayloadSize
+ )
+{
+ EFI_VARIABLE_AUTHENTICATION_2 *CertData;
+ UINT8 *SigData;
+ UINT32 SigDataSize;
+ UINT8 *PayloadPtr;
+ UINTN PayloadSize;
+ UINT32 Attr;
+ BOOLEAN VerifyStatus;
+ EFI_STATUS Status;
+ EFI_SIGNATURE_LIST *CertList;
+ EFI_SIGNATURE_DATA *Cert;
+ UINTN Index;
+ UINTN CertCount;
+ UINT32 KekDataSize;
+ UINT8 *NewData;
+ UINTN NewDataSize;
+ UINT8 *Buffer;
+ UINTN Length;
+ UINT8 *TopLevelCert;
+ UINTN TopLevelCertSize;
+ UINT8 *TrustedCert;
+ UINTN TrustedCertSize;
+ UINT8 *SignerCerts;
+ UINTN CertStackSize;
+ UINT8 *CertsInCertDb;
+ UINT32 CertsSizeinDb;
+ UINT8 Sha256Digest[SHA256_DIGEST_SIZE];
+ EFI_CERT_DATA *CertDataPtr;
+
+ //
+ // 1. TopLevelCert is the top-level issuer certificate in signature Signer Cert Chain
+ // 2. TrustedCert is the certificate which firmware trusts. It could be saved in protected
+ // storage or PK payload on PK init
+ //
+ VerifyStatus = FALSE;
+ CertData = NULL;
+ NewData = NULL;
+ Attr = Attributes;
+ SignerCerts = NULL;
+ TopLevelCert = NULL;
+ CertsInCertDb = NULL;
+ CertDataPtr = NULL;
+
+ //
+ // When the attribute EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS is
+ // set, then the Data buffer shall begin with an instance of a complete (and serialized)
+ // EFI_VARIABLE_AUTHENTICATION_2 descriptor. The descriptor shall be followed by the new
+ // variable value and DataSize shall reflect the combined size of the descriptor and the new
+ // variable value. The authentication descriptor is not part of the variable data and is not
+ // returned by subsequent calls to GetVariable().
+ //
+ CertData = (EFI_VARIABLE_AUTHENTICATION_2 *) Data;
+
+ //
+ // Verify that Pad1, Nanosecond, TimeZone, Daylight and Pad2 components of the
+ // TimeStamp value are set to zero.
+ //
+ if ((CertData->TimeStamp.Pad1 != 0) ||
+ (CertData->TimeStamp.Nanosecond != 0) ||
+ (CertData->TimeStamp.TimeZone != 0) ||
+ (CertData->TimeStamp.Daylight != 0) ||
+ (CertData->TimeStamp.Pad2 != 0)) {
+ return EFI_SECURITY_VIOLATION;
+ }
+
+ if ((OrgTimeStamp != NULL) && ((Attributes & EFI_VARIABLE_APPEND_WRITE) == 0)) {
+ if (AuthServiceInternalCompareTimeStamp (&CertData->TimeStamp, OrgTimeStamp)) {
+ //
+ // TimeStamp check fail, suspicious replay attack, return EFI_SECURITY_VIOLATION.
+ //
+ return EFI_SECURITY_VIOLATION;
+ }
+ }
+
+ //
+ // wCertificateType should be WIN_CERT_TYPE_EFI_GUID.
+ // Cert type should be EFI_CERT_TYPE_PKCS7_GUID.
+ //
+ if ((CertData->AuthInfo.Hdr.wCertificateType != WIN_CERT_TYPE_EFI_GUID) ||
+ !CompareGuid (&CertData->AuthInfo.CertType, &gEfiCertPkcs7Guid)) {
+ //
+ // Invalid AuthInfo type, return EFI_SECURITY_VIOLATION.
+ //
+ return EFI_SECURITY_VIOLATION;
+ }
+
+ //
+ // Find out Pkcs7 SignedData which follows the EFI_VARIABLE_AUTHENTICATION_2 descriptor.
+ // AuthInfo.Hdr.dwLength is the length of the entire certificate, including the length of the header.
+ //
+ SigData = CertData->AuthInfo.CertData;
+ SigDataSize = CertData->AuthInfo.Hdr.dwLength - (UINT32) (OFFSET_OF (WIN_CERTIFICATE_UEFI_GUID, CertData));
+
+ //
+ // SignedData.digestAlgorithms shall contain the digest algorithm used when preparing the
+ // signature. Only a digest algorithm of SHA-256 is accepted.
+ //
+ // According to PKCS#7 Definition:
+ // SignedData ::= SEQUENCE {
+ // version Version,
+ // digestAlgorithms DigestAlgorithmIdentifiers,
+ // contentInfo ContentInfo,
+ // .... }
+ // The DigestAlgorithmIdentifiers can be used to determine the hash algorithm
+ // in VARIABLE_AUTHENTICATION_2 descriptor.
+ // This field has the fixed offset (+13) and be calculated based on two bytes of length encoding.
+ //
+ if ((Attributes & EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS) != 0) {
+ if (SigDataSize >= (13 + sizeof (mSha256OidValue))) {
+ if (((*(SigData + 1) & TWO_BYTE_ENCODE) != TWO_BYTE_ENCODE) ||
+ (CompareMem (SigData + 13, &mSha256OidValue, sizeof (mSha256OidValue)) != 0)) {
+ return EFI_SECURITY_VIOLATION;
+ }
+ }
+ }
+
+ //
+ // Find out the new data payload which follows Pkcs7 SignedData directly.
+ //
+ PayloadPtr = SigData + SigDataSize;
+ PayloadSize = DataSize - OFFSET_OF_AUTHINFO2_CERT_DATA - (UINTN) SigDataSize;
+
+ // If the VariablePolicy engine is disabled, allow deletion of any authenticated variables.
+ if (PayloadSize == 0 && (Attributes & EFI_VARIABLE_APPEND_WRITE) == 0 && !IsVariablePolicyEnabled()) {
+ VerifyStatus = TRUE;
+ goto Exit;
+ }
+
+ //
+ // Construct a serialization buffer of the values of the VariableName, VendorGuid and Attributes
+ // parameters of the SetVariable() call and the TimeStamp component of the
+ // EFI_VARIABLE_AUTHENTICATION_2 descriptor followed by the variable's new value
+ // i.e. (VariableName, VendorGuid, Attributes, TimeStamp, Data)
+ //
+ NewDataSize = PayloadSize + sizeof (EFI_TIME) + sizeof (UINT32) +
+ sizeof (EFI_GUID) + StrSize (VariableName) - sizeof (CHAR16);
+
+ //
+ // Here is to reuse scratch data area(at the end of volatile variable store)
+ // to reduce SMRAM consumption for SMM variable driver.
+ // The scratch buffer is enough to hold the serialized data and safe to use,
+ // because it is only used at here to do verification temporarily first
+ // and then used in UpdateVariable() for a time based auth variable set.
+ //
+ Status = mAuthVarLibContextIn->GetScratchBuffer (&NewDataSize, (VOID **) &NewData);
+ if (EFI_ERROR (Status)) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ Buffer = NewData;
+ Length = StrLen (VariableName) * sizeof (CHAR16);
+ CopyMem (Buffer, VariableName, Length);
+ Buffer += Length;
+
+ Length = sizeof (EFI_GUID);
+ CopyMem (Buffer, VendorGuid, Length);
+ Buffer += Length;
+
+ Length = sizeof (UINT32);
+ CopyMem (Buffer, &Attr, Length);
+ Buffer += Length;
+
+ Length = sizeof (EFI_TIME);
+ CopyMem (Buffer, &CertData->TimeStamp, Length);
+ Buffer += Length;
+
+ CopyMem (Buffer, PayloadPtr, PayloadSize);
+
+ if (AuthVarType == AuthVarTypePk) {
+ //
+ // Verify that the signature has been made with the current Platform Key (no chaining for PK).
+ // First, get signer's certificates from SignedData.
+ //
+ VerifyStatus = Pkcs7GetSigners (
+ SigData,
+ SigDataSize,
+ &SignerCerts,
+ &CertStackSize,
+ &TopLevelCert,
+ &TopLevelCertSize
+ );
+ if (!VerifyStatus) {
+ goto Exit;
+ }
+
+ //
+ // Second, get the current platform key from variable. Check whether it's identical with signer's certificates
+ // in SignedData. If not, return error immediately.
+ //
+ Status = AuthServiceInternalFindVariable (
+ EFI_PLATFORM_KEY_NAME,
+ &gEfiGlobalVariableGuid,
+ &Data,
+ &DataSize
+ );
+ if (EFI_ERROR (Status)) {
+ VerifyStatus = FALSE;
+ goto Exit;
+ }
+ CertList = (EFI_SIGNATURE_LIST *) Data;
+ Cert = (EFI_SIGNATURE_DATA *) ((UINT8 *) CertList + sizeof (EFI_SIGNATURE_LIST) + CertList->SignatureHeaderSize);
+ if ((TopLevelCertSize != (CertList->SignatureSize - (sizeof (EFI_SIGNATURE_DATA) - 1))) ||
+ (CompareMem (Cert->SignatureData, TopLevelCert, TopLevelCertSize) != 0)) {
+ VerifyStatus = FALSE;
+ goto Exit;
+ }
+
+ //
+ // Verify Pkcs7 SignedData via Pkcs7Verify library.
+ //
+ VerifyStatus = Pkcs7Verify (
+ SigData,
+ SigDataSize,
+ TopLevelCert,
+ TopLevelCertSize,
+ NewData,
+ NewDataSize
+ );
+
+ } else if (AuthVarType == AuthVarTypeKek) {
+
+ //
+ // Get KEK database from variable.
+ //
+ Status = AuthServiceInternalFindVariable (
+ EFI_KEY_EXCHANGE_KEY_NAME,
+ &gEfiGlobalVariableGuid,
+ &Data,
+ &DataSize
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // Ready to verify Pkcs7 SignedData. Go through KEK Signature Database to find out X.509 CertList.
+ //
+ KekDataSize = (UINT32) DataSize;
+ CertList = (EFI_SIGNATURE_LIST *) Data;
+ while ((KekDataSize > 0) && (KekDataSize >= CertList->SignatureListSize)) {
+ if (CompareGuid (&CertList->SignatureType, &gEfiCertX509Guid)) {
+ Cert = (EFI_SIGNATURE_DATA *) ((UINT8 *) CertList + sizeof (EFI_SIGNATURE_LIST) + CertList->SignatureHeaderSize);
+ CertCount = (CertList->SignatureListSize - sizeof (EFI_SIGNATURE_LIST) - CertList->SignatureHeaderSize) / CertList->SignatureSize;
+ for (Index = 0; Index < CertCount; Index++) {
+ //
+ // Iterate each Signature Data Node within this CertList for a verify
+ //
+ TrustedCert = Cert->SignatureData;
+ TrustedCertSize = CertList->SignatureSize - (sizeof (EFI_SIGNATURE_DATA) - 1);
+
+ //
+ // Verify Pkcs7 SignedData via Pkcs7Verify library.
+ //
+ VerifyStatus = Pkcs7Verify (
+ SigData,
+ SigDataSize,
+ TrustedCert,
+ TrustedCertSize,
+ NewData,
+ NewDataSize
+ );
+ if (VerifyStatus) {
+ goto Exit;
+ }
+ Cert = (EFI_SIGNATURE_DATA *) ((UINT8 *) Cert + CertList->SignatureSize);
+ }
+ }
+ KekDataSize -= CertList->SignatureListSize;
+ CertList = (EFI_SIGNATURE_LIST *) ((UINT8 *) CertList + CertList->SignatureListSize);
+ }
+ } else if (AuthVarType == AuthVarTypePriv) {
+
+ //
+ // Process common authenticated variable except PK/KEK/DB/DBX/DBT.
+ // Get signer's certificates from SignedData.
+ //
+ VerifyStatus = Pkcs7GetSigners (
+ SigData,
+ SigDataSize,
+ &SignerCerts,
+ &CertStackSize,
+ &TopLevelCert,
+ &TopLevelCertSize
+ );
+ if (!VerifyStatus) {
+ goto Exit;
+ }
+
+ //
+ // Get previously stored signer's certificates from certdb or certdbv for existing
+ // variable. Check whether they are identical with signer's certificates
+ // in SignedData. If not, return error immediately.
+ //
+ if (OrgTimeStamp != NULL) {
+ VerifyStatus = FALSE;
+
+ Status = GetCertsFromDb (VariableName, VendorGuid, Attributes, &CertsInCertDb, &CertsSizeinDb);
+ if (EFI_ERROR (Status)) {
+ goto Exit;
+ }
+
+ if (CertsSizeinDb == SHA256_DIGEST_SIZE) {
+ //
+ // Check hash of signer cert CommonName + Top-level issuer tbsCertificate against data in CertDb
+ //
+ CertDataPtr = (EFI_CERT_DATA *)(SignerCerts + 1);
+ Status = CalculatePrivAuthVarSignChainSHA256Digest(
+ CertDataPtr->CertDataBuffer,
+ ReadUnaligned32 ((UINT32 *)&(CertDataPtr->CertDataLength)),
+ TopLevelCert,
+ TopLevelCertSize,
+ Sha256Digest
+ );
+ if (EFI_ERROR(Status) || CompareMem (Sha256Digest, CertsInCertDb, CertsSizeinDb) != 0){
+ goto Exit;
+ }
+ } else {
+ //
+ // Keep backward compatible with previous solution which saves whole signer certs stack in CertDb
+ //
+ if ((CertStackSize != CertsSizeinDb) ||
+ (CompareMem (SignerCerts, CertsInCertDb, CertsSizeinDb) != 0)) {
+ goto Exit;
+ }
+ }
+ }
+
+ VerifyStatus = Pkcs7Verify (
+ SigData,
+ SigDataSize,
+ TopLevelCert,
+ TopLevelCertSize,
+ NewData,
+ NewDataSize
+ );
+ if (!VerifyStatus) {
+ goto Exit;
+ }
+
+ if ((OrgTimeStamp == NULL) && (PayloadSize != 0)) {
+ //
+ // When adding a new common authenticated variable, always save Hash of cn of signer cert + tbsCertificate of Top-level issuer
+ //
+ CertDataPtr = (EFI_CERT_DATA *)(SignerCerts + 1);
+ Status = InsertCertsToDb (
+ VariableName,
+ VendorGuid,
+ Attributes,
+ CertDataPtr->CertDataBuffer,
+ ReadUnaligned32 ((UINT32 *)&(CertDataPtr->CertDataLength)),
+ TopLevelCert,
+ TopLevelCertSize
+ );
+ if (EFI_ERROR (Status)) {
+ VerifyStatus = FALSE;
+ goto Exit;
+ }
+ }
+ } else if (AuthVarType == AuthVarTypePayload) {
+ CertList = (EFI_SIGNATURE_LIST *) PayloadPtr;
+ Cert = (EFI_SIGNATURE_DATA *) ((UINT8 *) CertList + sizeof (EFI_SIGNATURE_LIST) + CertList->SignatureHeaderSize);
+ TrustedCert = Cert->SignatureData;
+ TrustedCertSize = CertList->SignatureSize - (sizeof (EFI_SIGNATURE_DATA) - 1);
+ //
+ // Verify Pkcs7 SignedData via Pkcs7Verify library.
+ //
+ VerifyStatus = Pkcs7Verify (
+ SigData,
+ SigDataSize,
+ TrustedCert,
+ TrustedCertSize,
+ NewData,
+ NewDataSize
+ );
+ } else {
+ return EFI_SECURITY_VIOLATION;
+ }
+
+Exit:
+
+ if (AuthVarType == AuthVarTypePk || AuthVarType == AuthVarTypePriv) {
+ if (TopLevelCert != NULL) {
+ Pkcs7FreeSigners (TopLevelCert);
+ }
+ if (SignerCerts != NULL) {
+ Pkcs7FreeSigners (SignerCerts);
+ }
+ }
+
+ if (!VerifyStatus) {
+ return EFI_SECURITY_VIOLATION;
+ }
+
+ Status = CheckSignatureListFormat(VariableName, VendorGuid, PayloadPtr, PayloadSize);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ *VarPayloadPtr = PayloadPtr;
+ *VarPayloadSize = PayloadSize;
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Process variable with EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS set
+
+ Caution: This function may receive untrusted input.
+ This function may be invoked in SMM mode, and datasize and data are external input.
+ This function will do basic validation, before parse the data.
+ This function will parse the authentication carefully to avoid security issues, like
+ buffer overflow, integer overflow.
+
+ @param[in] VariableName Name of Variable to be found.
+ @param[in] VendorGuid Variable vendor GUID.
+ @param[in] Data Data pointer.
+ @param[in] DataSize Size of Data found. If size is less than the
+ data, this value contains the required size.
+ @param[in] Attributes Attribute value of the variable.
+ @param[in] AuthVarType Verify against PK, KEK database, private database or certificate in data payload.
+ @param[out] VarDel Delete the variable or not.
+
+ @retval EFI_INVALID_PARAMETER Invalid parameter.
+ @retval EFI_SECURITY_VIOLATION The variable does NOT pass the validation
+ check carried out by the firmware.
+ @retval EFI_OUT_OF_RESOURCES Failed to process variable due to lack
+ of resources.
+ @retval EFI_SUCCESS Variable pass validation successfully.
+
+**/
+EFI_STATUS
+VerifyTimeBasedPayloadAndUpdate (
+ IN CHAR16 *VariableName,
+ IN EFI_GUID *VendorGuid,
+ IN VOID *Data,
+ IN UINTN DataSize,
+ IN UINT32 Attributes,
+ IN AUTHVAR_TYPE AuthVarType,
+ OUT BOOLEAN *VarDel
+ )
+{
+ EFI_STATUS Status;
+ EFI_STATUS FindStatus;
+ UINT8 *PayloadPtr;
+ UINTN PayloadSize;
+ EFI_VARIABLE_AUTHENTICATION_2 *CertData;
+ AUTH_VARIABLE_INFO OrgVariableInfo;
+ BOOLEAN IsDel;
+
+ ZeroMem (&OrgVariableInfo, sizeof (OrgVariableInfo));
+ FindStatus = mAuthVarLibContextIn->FindVariable (
+ VariableName,
+ VendorGuid,
+ &OrgVariableInfo
+ );
+
+ Status = VerifyTimeBasedPayload (
+ VariableName,
+ VendorGuid,
+ Data,
+ DataSize,
+ Attributes,
+ AuthVarType,
+ (!EFI_ERROR (FindStatus)) ? OrgVariableInfo.TimeStamp : NULL,
+ &PayloadPtr,
+ &PayloadSize
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ if (!EFI_ERROR(FindStatus)
+ && (PayloadSize == 0)
+ && ((Attributes & EFI_VARIABLE_APPEND_WRITE) == 0)) {
+ IsDel = TRUE;
+ } else {
+ IsDel = FALSE;
+ }
+
+ CertData = (EFI_VARIABLE_AUTHENTICATION_2 *) Data;
+
+ //
+ // Final step: Update/Append Variable if it pass Pkcs7Verify
+ //
+ Status = AuthServiceInternalUpdateVariableWithTimeStamp (
+ VariableName,
+ VendorGuid,
+ PayloadPtr,
+ PayloadSize,
+ Attributes,
+ &CertData->TimeStamp
+ );
+
+ //
+ // Delete signer's certificates when delete the common authenticated variable.
+ //
+ if (IsDel && AuthVarType == AuthVarTypePriv && !EFI_ERROR(Status) ) {
+ Status = DeleteCertsFromDb (VariableName, VendorGuid, Attributes);
+ }
+
+ if (VarDel != NULL) {
+ if (IsDel && !EFI_ERROR(Status)) {
+ *VarDel = TRUE;
+ } else {
+ *VarDel = FALSE;
+ }
+ }
+
+ return Status;
+}