diff options
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.c | 2318 |
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; +} |