/** This code gets the pointer to the next variable header. @param StoreInfo Pointer to variable store info structure. @param Variable Pointer to the Variable Header. @param VariableHeader Pointer to the Variable Header that has consecutive content. @return A VARIABLE_HEADER* pointer to next variable header. **/ VARIABLE_HEADER * GetNextVariablePtr ( IN VARIABLE_STORE_INFO *StoreInfo, IN VARIABLE_HEADER *Variable, IN VARIABLE_HEADER *VariableHeader ) { EFI_PHYSICAL_ADDRESS TargetAddress; EFI_PHYSICAL_ADDRESS SpareAddress; UINTN Value; Value = (UINTN) GetVariableDataPtr (Variable, VariableHeader, StoreInfo->AuthFlag); Value += DataSizeOfVariable (VariableHeader, StoreInfo->AuthFlag); Value += GET_PAD_SIZE (DataSizeOfVariable (VariableHeader, StoreInfo->AuthFlag)); // // Be careful about pad size for alignment // Value = HEADER_ALIGN (Value); if (StoreInfo->FtwLastWriteData != NULL) { TargetAddress = StoreInfo->FtwLastWriteData->TargetAddress; SpareAddress = StoreInfo->FtwLastWriteData->SpareAddress; if (((UINTN) Variable < (UINTN) TargetAddress) && (Value >= (UINTN) TargetAddress)) { // // Next variable is in spare block. // Value = (UINTN) SpareAddress + (Value - (UINTN) TargetAddress); } } return (VARIABLE_HEADER *) Value; }
/** This service retrieves a variable's value using its name and GUID. Read the specified variable from the UEFI variable store. If the Data buffer is too small to hold the contents of the variable, the error EFI_BUFFER_TOO_SMALL is returned and DataSize is set to the required buffer size to obtain the data. @param This A pointer to this instance of the EFI_PEI_READ_ONLY_VARIABLE2_PPI. @param VariableName A pointer to a null-terminated string that is the variable's name. @param VariableGuid A pointer to an EFI_GUID that is the variable's GUID. The combination of VariableGuid and VariableName must be unique. @param Attributes If non-NULL, on return, points to the variable's attributes. @param DataSize On entry, points to the size in bytes of the Data buffer. On return, points to the size of the data returned in Data. @param Data Points to the buffer which will hold the returned variable value. @retval EFI_SUCCESS The variable was read successfully. @retval EFI_NOT_FOUND The variable could not be found. @retval EFI_BUFFER_TOO_SMALL The DataSize is too small for the resulting data. DataSize is updated with the size required for the specified variable. @retval EFI_INVALID_PARAMETER VariableName, VariableGuid, DataSize or Data is NULL. @retval EFI_DEVICE_ERROR The variable could not be retrieved because of a device error. **/ EFI_STATUS EFIAPI PeiGetVariable ( IN CONST EFI_PEI_READ_ONLY_VARIABLE2_PPI *This, IN CONST CHAR16 *VariableName, IN CONST EFI_GUID *VariableGuid, OUT UINT32 *Attributes, IN OUT UINTN *DataSize, OUT VOID *Data ) { VARIABLE_POINTER_TRACK Variable; UINTN VarDataSize; EFI_STATUS Status; VARIABLE_STORE_INFO StoreInfo; VARIABLE_HEADER *VariableHeader; if (VariableName == NULL || VariableGuid == NULL || DataSize == NULL) { return EFI_INVALID_PARAMETER; } VariableHeader = NULL; // // Find existing variable // Status = FindVariable (VariableName, VariableGuid, &Variable, &StoreInfo); if (EFI_ERROR (Status)) { return Status; } GetVariableHeader (&StoreInfo, Variable.CurrPtr, &VariableHeader); // // Get data size // VarDataSize = DataSizeOfVariable (VariableHeader, StoreInfo.AuthFlag); if (*DataSize >= VarDataSize) { if (Data == NULL) { return EFI_INVALID_PARAMETER; } GetVariableNameOrData (&StoreInfo, GetVariableDataPtr (Variable.CurrPtr, VariableHeader, StoreInfo.AuthFlag), VarDataSize, Data); if (Attributes != NULL) { *Attributes = VariableHeader->Attributes; } *DataSize = VarDataSize; return EFI_SUCCESS; } else { *DataSize = VarDataSize; return EFI_BUFFER_TOO_SMALL; } }
/** Finds next variable in storage blocks of volatile and non-volatile storage areas. This code finds next 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] AuthVariableInfo Pointer to AUTH_VARIABLE_INFO structure for output of the next variable. @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 EFIAPI VariableExLibFindNextVariable ( IN CHAR16 *VariableName, IN EFI_GUID *VendorGuid, OUT AUTH_VARIABLE_INFO *AuthVariableInfo ) { EFI_STATUS Status; VARIABLE_HEADER *VariablePtr; AUTHENTICATED_VARIABLE_HEADER *AuthVariablePtr; Status = VariableServiceGetNextVariableInternal ( VariableName, VendorGuid, &VariablePtr ); if (EFI_ERROR (Status)) { AuthVariableInfo->VariableName = NULL; AuthVariableInfo->VendorGuid = NULL; AuthVariableInfo->Data = NULL; AuthVariableInfo->DataSize = 0; AuthVariableInfo->Attributes = 0; AuthVariableInfo->PubKeyIndex = 0; AuthVariableInfo->MonotonicCount = 0; AuthVariableInfo->TimeStamp = NULL; return Status; } AuthVariableInfo->VariableName = GetVariableNamePtr (VariablePtr); AuthVariableInfo->VendorGuid = GetVendorGuidPtr (VariablePtr); AuthVariableInfo->DataSize = DataSizeOfVariable (VariablePtr); AuthVariableInfo->Data = GetVariableDataPtr (VariablePtr); AuthVariableInfo->Attributes = VariablePtr->Attributes; if (mVariableModuleGlobal->VariableGlobal.AuthFormat) { AuthVariablePtr = (AUTHENTICATED_VARIABLE_HEADER *) VariablePtr; AuthVariableInfo->PubKeyIndex = AuthVariablePtr->PubKeyIndex; AuthVariableInfo->MonotonicCount = AuthVariablePtr->MonotonicCount; AuthVariableInfo->TimeStamp = &AuthVariablePtr->TimeStamp; } return EFI_SUCCESS; }
/** This code gets the pointer to the next variable header. @param Variable Pointer to the Variable Header. @return A VARIABLE_HEADER* pointer to next variable header. **/ VARIABLE_HEADER * GetNextVariablePtr ( IN VARIABLE_HEADER *Variable ) { UINTN Value; if (!IsValidVariableHeader (Variable)) { return NULL; } Value = (UINTN) GetVariableDataPtr (Variable); Value += DataSizeOfVariable (Variable); Value += GET_PAD_SIZE (DataSizeOfVariable (Variable)); // // Be careful about pad size for alignment // return (VARIABLE_HEADER *) HEADER_ALIGN (Value); }
/** 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] AuthVariableInfo Pointer to AUTH_VARIABLE_INFO structure for output of the variable found. @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 EFIAPI VariableExLibFindVariable ( IN CHAR16 *VariableName, IN EFI_GUID *VendorGuid, OUT AUTH_VARIABLE_INFO *AuthVariableInfo ) { EFI_STATUS Status; VARIABLE_POINTER_TRACK Variable; AUTHENTICATED_VARIABLE_HEADER *AuthVariable; Status = FindVariable ( VariableName, VendorGuid, &Variable, &mVariableModuleGlobal->VariableGlobal, FALSE ); if (EFI_ERROR (Status)) { AuthVariableInfo->Data = NULL; AuthVariableInfo->DataSize = 0; AuthVariableInfo->Attributes = 0; AuthVariableInfo->PubKeyIndex = 0; AuthVariableInfo->MonotonicCount = 0; AuthVariableInfo->TimeStamp = NULL; return Status; } AuthVariableInfo->DataSize = DataSizeOfVariable (Variable.CurrPtr); AuthVariableInfo->Data = GetVariableDataPtr (Variable.CurrPtr); AuthVariableInfo->Attributes = Variable.CurrPtr->Attributes; if (mVariableModuleGlobal->VariableGlobal.AuthFormat) { AuthVariable = (AUTHENTICATED_VARIABLE_HEADER *) Variable.CurrPtr; AuthVariableInfo->PubKeyIndex = AuthVariable->PubKeyIndex; AuthVariableInfo->MonotonicCount = AuthVariable->MonotonicCount; AuthVariableInfo->TimeStamp = &AuthVariable->TimeStamp; } return EFI_SUCCESS; }
/** Process variable with key exchange key for verification. @param[in] VariableName The name of Variable to be found. @param[in] VendorGuid The variable vendor GUID. @param[in] Data The data pointer. @param[in] DataSize The size of Data found. If size is less than the data, this value contains the required size. @param[in] VirtualMode The current calling mode for this function. @param[in] Global The context of this Extended SAL Variable Services Class call. @param[in] Variable The variable information which is used to keep track of variable usage. @param[in] Attributes The attribute value of the variable. @retval EFI_INVALID_PARAMETER Invalid parameter. @retval EFI_SECURITY_VIOLATION The variable did NOT pass the validation check carried out by the firmware. @retval EFI_SUCCESS The variable passed validation successfully. **/ EFI_STATUS ProcessVarWithKek ( IN CHAR16 *VariableName, IN EFI_GUID *VendorGuid, IN VOID *Data, IN UINTN DataSize, IN BOOLEAN VirtualMode, IN ESAL_VARIABLE_GLOBAL *Global, IN VARIABLE_POINTER_TRACK *Variable, IN UINT32 Attributes OPTIONAL ) { EFI_STATUS Status; VARIABLE_POINTER_TRACK KekVariable; EFI_SIGNATURE_LIST *KekList; EFI_SIGNATURE_DATA *KekItem; UINT32 KekCount; EFI_VARIABLE_AUTHENTICATION *CertData; EFI_CERT_BLOCK_RSA_2048_SHA256 *CertBlock; BOOLEAN IsFound; UINT32 Index; VARIABLE_HEADER VariableHeader; BOOLEAN Valid; KekList = NULL; ZeroMem (&VariableHeader, sizeof (VARIABLE_HEADER)); if (mPlatformMode == USER_MODE) { if ((Attributes & EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS) == 0) { // // In user mode, should set EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS attribute. // return EFI_INVALID_PARAMETER; } CertData = (EFI_VARIABLE_AUTHENTICATION *) Data; CertBlock = (EFI_CERT_BLOCK_RSA_2048_SHA256 *) (CertData->AuthInfo.CertData); if (Variable->CurrPtr != 0x0) { Valid = IsValidVariableHeader ( Variable->CurrPtr, Variable->Volatile, &Global->VariableGlobal[VirtualMode], Global->FvbInstance, &VariableHeader ); ASSERT (Valid); if (CertData->MonotonicCount <= VariableHeader.MonotonicCount) { // // Monotonic count check fail, suspicious replay attack, return EFI_SECURITY_VIOLATION. // return EFI_SECURITY_VIOLATION; } } // // Get KEK database from variable. // Status = FindVariable ( Global->VariableName[VirtualMode][VAR_KEY_EXCHANGE_KEY], Global->GlobalVariableGuid[VirtualMode], &KekVariable, &Global->VariableGlobal[VirtualMode], Global->FvbInstance ); ASSERT_EFI_ERROR (Status); ZeroMem (Global->KeyList, MAX_KEYDB_SIZE); GetVariableDataPtr ( KekVariable.CurrPtr, KekVariable.Volatile, &Global->VariableGlobal[VirtualMode], Global->FvbInstance, (CHAR16 *) Global->KeyList ); // // Enumerate all Kek items in this list to verify the variable certificate data. // If anyone is authenticated successfully, it means the variable is correct! // KekList = (EFI_SIGNATURE_LIST *) Global->KeyList; IsFound = FALSE; KekCount = (KekList->SignatureListSize - sizeof (EFI_SIGNATURE_LIST) - KekList->SignatureHeaderSize) / KekList->SignatureSize; KekItem = (EFI_SIGNATURE_DATA *) ((UINT8 *) KekList + sizeof (EFI_SIGNATURE_LIST) + KekList->SignatureHeaderSize); for (Index = 0; Index < KekCount; Index++) { if (CompareMem (KekItem->SignatureData, CertBlock->PublicKey, EFI_CERT_TYPE_RSA2048_SIZE) == 0) { IsFound = TRUE; break; } KekItem = (EFI_SIGNATURE_DATA *) ((UINT8 *) KekItem + KekList->SignatureSize); } if (!IsFound) { return EFI_SECURITY_VIOLATION; } Status = VerifyDataPayload (VirtualMode, Global, Data, DataSize, CertBlock->PublicKey); if (!EFI_ERROR (Status)) { Status = UpdateVariable ( VariableName, VendorGuid, (UINT8*)Data + AUTHINFO_SIZE, DataSize - AUTHINFO_SIZE, Attributes, 0, CertData->MonotonicCount, VirtualMode, Global, Variable ); } } else { // // If in setup mode, no authentication needed. // Status = UpdateVariable ( VariableName, VendorGuid, Data, DataSize, Attributes, 0, 0, VirtualMode, Global, Variable ); } return Status; }
/** Process variable with platform key for verification. @param[in] VariableName The name of Variable to be found. @param[in] VendorGuid The variable vendor GUID. @param[in] Data The data pointer. @param[in] DataSize The size of Data found. If size is less than the data, this value contains the required size. @param[in] VirtualMode The current calling mode for this function. @param[in] Global The context of this Extended SAL Variable Services Class call. @param[in] Variable The variable information which is used to keep track of variable usage. @param[in] Attributes The attribute value of the variable. @param[in] IsPk Indicates whether to process pk. @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_SUCCESS The variable passed validation successfully. **/ EFI_STATUS ProcessVarWithPk ( IN CHAR16 *VariableName, IN EFI_GUID *VendorGuid, IN VOID *Data, IN UINTN DataSize, IN BOOLEAN VirtualMode, IN ESAL_VARIABLE_GLOBAL *Global, IN VARIABLE_POINTER_TRACK *Variable, IN UINT32 Attributes OPTIONAL, IN BOOLEAN IsPk ) { EFI_STATUS Status; VARIABLE_POINTER_TRACK PkVariable; EFI_SIGNATURE_LIST *OldPkList; EFI_SIGNATURE_DATA *OldPkData; EFI_VARIABLE_AUTHENTICATION *CertData; VARIABLE_HEADER VariableHeader; BOOLEAN Valid; OldPkList = NULL; ZeroMem (&VariableHeader, sizeof (VARIABLE_HEADER)); if ((Attributes & EFI_VARIABLE_NON_VOLATILE) == 0) { // // PK and KEK should set EFI_VARIABLE_NON_VOLATILE attribute. // return EFI_INVALID_PARAMETER; } if (mPlatformMode == USER_MODE) { if ((Attributes & EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS) == 0) { // // In user mode, PK and KEK should set EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS attribute. // return EFI_INVALID_PARAMETER; } CertData = (EFI_VARIABLE_AUTHENTICATION *) Data; if (Variable->CurrPtr != 0x0) { Valid = IsValidVariableHeader ( Variable->CurrPtr, Variable->Volatile, &Global->VariableGlobal[VirtualMode], Global->FvbInstance, &VariableHeader ); ASSERT (Valid); if (CertData->MonotonicCount <= VariableHeader.MonotonicCount) { // // Monotonic count check fail, suspicious replay attack, return EFI_SECURITY_VIOLATION. // return EFI_SECURITY_VIOLATION; } } // // Get platform key from variable. // Status = FindVariable ( Global->VariableName[VirtualMode][VAR_PLATFORM_KEY], Global->GlobalVariableGuid[VirtualMode], &PkVariable, &Global->VariableGlobal[VirtualMode], Global->FvbInstance ); ASSERT_EFI_ERROR (Status); ZeroMem (Global->KeyList, MAX_KEYDB_SIZE); GetVariableDataPtr ( PkVariable.CurrPtr, PkVariable.Volatile, &Global->VariableGlobal[VirtualMode], Global->FvbInstance, (CHAR16 *) Global->KeyList ); OldPkList = (EFI_SIGNATURE_LIST *) Global->KeyList; OldPkData = (EFI_SIGNATURE_DATA *) ((UINT8 *) OldPkList + sizeof (EFI_SIGNATURE_LIST) + OldPkList->SignatureHeaderSize); Status = VerifyDataPayload (VirtualMode, Global, Data, DataSize, OldPkData->SignatureData); if (!EFI_ERROR (Status)) { Status = UpdateVariable ( VariableName, VendorGuid, (UINT8*)Data + AUTHINFO_SIZE, DataSize - AUTHINFO_SIZE, Attributes, 0, CertData->MonotonicCount, VirtualMode, Global, Variable ); if (!EFI_ERROR (Status)) { // // If delete PK in user mode, need change to setup mode. // if ((DataSize == AUTHINFO_SIZE) && IsPk) { UpdatePlatformMode (VirtualMode, Global, SETUP_MODE); } } } } else { Status = UpdateVariable (VariableName, VendorGuid, Data, DataSize, Attributes, 0, 0, VirtualMode, Global, Variable); // // If enroll PK in setup mode, need change to user mode. // if ((DataSize != 0) && IsPk) { UpdatePlatformMode (VirtualMode, Global, USER_MODE); } } return Status; }
/** Initializes for authenticated varibale service. @retval EFI_SUCCESS The function successfully executed. @retval EFI_OUT_OF_RESOURCES Failed to allocate enough memory resources. **/ EFI_STATUS AutenticatedVariableServiceInitialize ( VOID ) { EFI_STATUS Status; VARIABLE_POINTER_TRACK Variable; UINT8 VarValue; UINT32 VarAttr; UINTN DataSize; UINTN CtxSize; VARIABLE_HEADER VariableHeader; BOOLEAN Valid; ZeroMem (&VariableHeader, sizeof (VARIABLE_HEADER)); mVariableModuleGlobal->AuthenticatedVariableGuid[Physical] = &gEfiAuthenticatedVariableGuid; mVariableModuleGlobal->CertRsa2048Sha256Guid[Physical] = &gEfiCertRsa2048Sha256Guid; mVariableModuleGlobal->ImageSecurityDatabaseGuid[Physical] = &gEfiImageSecurityDatabaseGuid; // // Initialize hash context. // CtxSize = Sha256GetContextSize (); mVariableModuleGlobal->HashContext[Physical] = AllocateRuntimePool (CtxSize); ASSERT (mVariableModuleGlobal->HashContext[Physical] != NULL); // // Check "AuthVarKeyDatabase" variable's existence. // If it doesn't exist, create a new one with initial value of 0 and EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS set. // Status = FindVariable ( mVariableModuleGlobal->VariableName[Physical][VAR_AUTH_KEY_DB], &gEfiAuthenticatedVariableGuid, &Variable, &mVariableModuleGlobal->VariableGlobal[Physical], mVariableModuleGlobal->FvbInstance ); if (Variable.CurrPtr == 0x0) { VarAttr = EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS; VarValue = 0; mPubKeyNumber = 0; Status = UpdateVariable ( mVariableModuleGlobal->VariableName[Physical][VAR_AUTH_KEY_DB], &gEfiAuthenticatedVariableGuid, &VarValue, sizeof(UINT8), VarAttr, 0, 0, FALSE, mVariableModuleGlobal, &Variable ); if (EFI_ERROR (Status)) { return Status; } } else { // // Load database in global variable for cache. // Valid = IsValidVariableHeader ( Variable.CurrPtr, Variable.Volatile, &mVariableModuleGlobal->VariableGlobal[Physical], mVariableModuleGlobal->FvbInstance, &VariableHeader ); ASSERT (Valid); DataSize = DataSizeOfVariable (&VariableHeader); ASSERT (DataSize <= MAX_KEYDB_SIZE); GetVariableDataPtr ( Variable.CurrPtr, Variable.Volatile, &mVariableModuleGlobal->VariableGlobal[Physical], mVariableModuleGlobal->FvbInstance, (CHAR16 *) mVariableModuleGlobal->PubKeyStore ); mPubKeyNumber = (UINT32) (DataSize / EFI_CERT_TYPE_RSA2048_SIZE); } // // Check "SetupMode" variable's existence. // If it doesn't exist, check PK database's existence to determine the value. // Then create a new one with EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS set. // Status = FindVariable ( mVariableModuleGlobal->VariableName[Physical][VAR_SETUP_MODE], &gEfiGlobalVariableGuid, &Variable, &mVariableModuleGlobal->VariableGlobal[Physical], mVariableModuleGlobal->FvbInstance ); if (Variable.CurrPtr == 0x0) { Status = FindVariable ( mVariableModuleGlobal->VariableName[Physical][VAR_PLATFORM_KEY], &gEfiGlobalVariableGuid, &Variable, &mVariableModuleGlobal->VariableGlobal[Physical], mVariableModuleGlobal->FvbInstance ); if (Variable.CurrPtr == 0x0) { mPlatformMode = SETUP_MODE; } else { mPlatformMode = USER_MODE; } VarAttr = EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS; Status = UpdateVariable ( mVariableModuleGlobal->VariableName[Physical][VAR_SETUP_MODE], &gEfiGlobalVariableGuid, &mPlatformMode, sizeof(UINT8), VarAttr, 0, 0, FALSE, mVariableModuleGlobal, &Variable ); if (EFI_ERROR (Status)) { return Status; } } else { GetVariableDataPtr ( Variable.CurrPtr, Variable.Volatile, &mVariableModuleGlobal->VariableGlobal[Physical], mVariableModuleGlobal->FvbInstance, (CHAR16 *) &mPlatformMode ); } // // Check "SignatureSupport" variable's existence. // If it doesn't exist, then create a new one with EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS set. // Status = FindVariable ( EFI_SIGNATURE_SUPPORT_NAME, &gEfiGlobalVariableGuid, &Variable, &mVariableModuleGlobal->VariableGlobal[Physical], mVariableModuleGlobal->FvbInstance ); if (Variable.CurrPtr == 0x0) { VarAttr = EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS; Status = UpdateVariable ( EFI_SIGNATURE_SUPPORT_NAME, &gEfiGlobalVariableGuid, mSignatureSupport, SIGSUPPORT_NUM * sizeof(EFI_GUID), VarAttr, 0, 0, FALSE, mVariableModuleGlobal, &Variable ); } return Status; }