/** @file Implementation functions and structures for var check services. Copyright (c) 2015 - 2021, Intel Corporation. All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent **/ #include #include #include #include #include #include #include BOOLEAN mVarCheckLibEndOfDxe = FALSE; #define VAR_CHECK_TABLE_SIZE 0x8 UINTN mVarCheckLibEndOfDxeCallbackCount = 0; UINTN mVarCheckLibEndOfDxeCallbackMaxCount = 0; VAR_CHECK_END_OF_DXE_CALLBACK *mVarCheckLibEndOfDxeCallback = NULL; UINTN mVarCheckLibAddressPointerCount = 0; UINTN mVarCheckLibAddressPointerMaxCount = 0; VOID ***mVarCheckLibAddressPointer = NULL; UINTN mNumberOfVarCheckHandler = 0; UINTN mMaxNumberOfVarCheckHandler = 0; VAR_CHECK_SET_VARIABLE_CHECK_HANDLER *mVarCheckHandlerTable = NULL; typedef struct { EFI_GUID Guid; VAR_CHECK_VARIABLE_PROPERTY VariableProperty; // CHAR16 *Name; } VAR_CHECK_VARIABLE_ENTRY; UINTN mNumberOfVarCheckVariable = 0; UINTN mMaxNumberOfVarCheckVariable = 0; VARIABLE_ENTRY_PROPERTY **mVarCheckVariableTable = NULL; // // Handle variables with wildcard name specially. // VARIABLE_ENTRY_PROPERTY mVarCheckVariableWithWildcardName[] = { { &gEfiGlobalVariableGuid, L"Boot####", { 0 }, }, { &gEfiGlobalVariableGuid, L"Driver####", { 0 }, }, { &gEfiGlobalVariableGuid, L"SysPrep####", { 0 }, }, { &gEfiGlobalVariableGuid, L"Key####", { 0 }, }, { &gEfiGlobalVariableGuid, L"PlatformRecovery####", { 0 }, }, { &gEfiHardwareErrorVariableGuid, L"HwErrRec####", { 0 }, }, }; /** Check if a Unicode character is an upper case hexadecimal character. This function checks if a Unicode character is an upper case hexadecimal character. The valid upper case hexadecimal character is L'0' to L'9', or L'A' to L'F'. @param[in] Char The character to check against. @retval TRUE If the Char is an upper case hexadecmial character. @retval FALSE If the Char is not an upper case hexadecmial character. **/ BOOLEAN EFIAPI VarCheckInternalIsHexaDecimalDigitCharacter ( IN CHAR16 Char ) { return (BOOLEAN)((Char >= L'0' && Char <= L'9') || (Char >= L'A' && Char <= L'F')); } /** Variable property get with wildcard name. @param[in] VariableName Pointer to variable name. @param[in] VendorGuid Pointer to variable vendor GUID. @param[in] WildcardMatch Try wildcard match or not. @return Pointer to variable property. **/ VAR_CHECK_VARIABLE_PROPERTY * VariablePropertyGetWithWildcardName ( IN CHAR16 *VariableName, IN EFI_GUID *VendorGuid, IN BOOLEAN WildcardMatch ) { UINTN Index; UINTN NameLength; NameLength = StrLen (VariableName) - 4; for (Index = 0; Index < sizeof (mVarCheckVariableWithWildcardName)/sizeof (mVarCheckVariableWithWildcardName[0]); Index++) { if (CompareGuid (mVarCheckVariableWithWildcardName[Index].Guid, VendorGuid)) { if (WildcardMatch) { if ((StrLen (VariableName) == StrLen (mVarCheckVariableWithWildcardName[Index].Name)) && (StrnCmp (VariableName, mVarCheckVariableWithWildcardName[Index].Name, NameLength) == 0) && VarCheckInternalIsHexaDecimalDigitCharacter (VariableName[NameLength]) && VarCheckInternalIsHexaDecimalDigitCharacter (VariableName[NameLength + 1]) && VarCheckInternalIsHexaDecimalDigitCharacter (VariableName[NameLength + 2]) && VarCheckInternalIsHexaDecimalDigitCharacter (VariableName[NameLength + 3])) { return &mVarCheckVariableWithWildcardName[Index].VariableProperty; } } if (StrCmp (mVarCheckVariableWithWildcardName[Index].Name, VariableName) == 0) { return &mVarCheckVariableWithWildcardName[Index].VariableProperty; } } } return NULL; } /** Variable property get function. @param[in] Name Pointer to the variable name. @param[in] Guid Pointer to the vendor GUID. @param[in] WildcardMatch Try wildcard match or not. @return Pointer to the property of variable specified by the Name and Guid. **/ VAR_CHECK_VARIABLE_PROPERTY * VariablePropertyGetFunction ( IN CHAR16 *Name, IN EFI_GUID *Guid, IN BOOLEAN WildcardMatch ) { UINTN Index; VAR_CHECK_VARIABLE_ENTRY *Entry; CHAR16 *VariableName; for (Index = 0; Index < mNumberOfVarCheckVariable; Index++) { Entry = (VAR_CHECK_VARIABLE_ENTRY *)mVarCheckVariableTable[Index]; VariableName = (CHAR16 *)((UINTN)Entry + sizeof (*Entry)); if (CompareGuid (&Entry->Guid, Guid) && (StrCmp (VariableName, Name) == 0)) { return &Entry->VariableProperty; } } return VariablePropertyGetWithWildcardName (Name, Guid, WildcardMatch); } /** Var check add table entry. @param[in, out] Table Pointer to table buffer. @param[in, out] MaxNumber Pointer to maximum number of entry in the table. @param[in, out] CurrentNumber Pointer to current number of entry in the table. @param[in] Entry Entry will be added to the table. @retval EFI_SUCCESS Reallocate memory successfully. @retval EFI_OUT_OF_RESOURCES No enough memory to allocate. **/ EFI_STATUS VarCheckAddTableEntry ( IN OUT UINTN **Table, IN OUT UINTN *MaxNumber, IN OUT UINTN *CurrentNumber, IN UINTN Entry ) { UINTN *TempTable; // // Check whether the table is enough to store new entry. // if (*CurrentNumber == *MaxNumber) { // // Reallocate memory for the table. // TempTable = ReallocateRuntimePool ( *MaxNumber * sizeof (UINTN), (*MaxNumber + VAR_CHECK_TABLE_SIZE) * sizeof (UINTN), *Table ); // // No enough resource to allocate. // if (TempTable == NULL) { return EFI_OUT_OF_RESOURCES; } *Table = TempTable; // // Increase max number. // *MaxNumber += VAR_CHECK_TABLE_SIZE; } // // Add entry to the table. // (*Table)[*CurrentNumber] = Entry; (*CurrentNumber)++; return EFI_SUCCESS; } /** Register END_OF_DXE callback. The callback will be invoked by VarCheckLibInitializeAtEndOfDxe(). @param[in] Callback END_OF_DXE callback. @retval EFI_SUCCESS The callback was registered successfully. @retval EFI_INVALID_PARAMETER Callback is NULL. @retval EFI_ACCESS_DENIED EFI_END_OF_DXE_EVENT_GROUP_GUID or EFI_EVENT_GROUP_READY_TO_BOOT has already been signaled. @retval EFI_OUT_OF_RESOURCES There is not enough resource for the callback register request. **/ EFI_STATUS EFIAPI VarCheckLibRegisterEndOfDxeCallback ( IN VAR_CHECK_END_OF_DXE_CALLBACK Callback ) { EFI_STATUS Status; if (Callback == NULL) { return EFI_INVALID_PARAMETER; } if (mVarCheckLibEndOfDxe) { return EFI_ACCESS_DENIED; } Status = VarCheckAddTableEntry ( (UINTN **)&mVarCheckLibEndOfDxeCallback, &mVarCheckLibEndOfDxeCallbackMaxCount, &mVarCheckLibEndOfDxeCallbackCount, (UINTN)Callback ); DEBUG ((DEBUG_INFO, "VarCheckLibRegisterEndOfDxeCallback - 0x%x %r\n", Callback, Status)); return Status; } /** Var check initialize at END_OF_DXE. This function needs to be called at END_OF_DXE. Address pointers may be returned, and caller needs to ConvertPointer() for the pointers. @param[in, out] AddressPointerCount Output pointer to address pointer count. @return Address pointer buffer, NULL if input AddressPointerCount is NULL. **/ VOID *** EFIAPI VarCheckLibInitializeAtEndOfDxe ( IN OUT UINTN *AddressPointerCount OPTIONAL ) { VOID *TempTable; UINTN TotalCount; UINTN Index; for (Index = 0; Index < mVarCheckLibEndOfDxeCallbackCount; Index++) { // // Invoke the callback registered by VarCheckLibRegisterEndOfDxeCallback(). // mVarCheckLibEndOfDxeCallback[Index](); } if (mVarCheckLibEndOfDxeCallback != NULL) { // // Free the callback buffer. // mVarCheckLibEndOfDxeCallbackCount = 0; mVarCheckLibEndOfDxeCallbackMaxCount = 0; FreePool ((VOID *)mVarCheckLibEndOfDxeCallback); mVarCheckLibEndOfDxeCallback = NULL; } mVarCheckLibEndOfDxe = TRUE; if (AddressPointerCount == NULL) { if (mVarCheckLibAddressPointer != NULL) { // // Free the address pointer buffer. // mVarCheckLibAddressPointerCount = 0; mVarCheckLibAddressPointerMaxCount = 0; FreePool ((VOID *)mVarCheckLibAddressPointer); mVarCheckLibAddressPointer = NULL; } return NULL; } // // Get the total count needed. // Also cover VarCheckHandler and the entries, and VarCheckVariable and the entries. // TotalCount = mVarCheckLibAddressPointerCount + (mNumberOfVarCheckHandler + 1) + (mNumberOfVarCheckVariable + 1); TempTable = ReallocateRuntimePool ( mVarCheckLibAddressPointerMaxCount * sizeof (VOID **), TotalCount * sizeof (VOID **), (VOID *)mVarCheckLibAddressPointer ); if (TempTable != NULL) { mVarCheckLibAddressPointer = (VOID ***)TempTable; // // Cover VarCheckHandler and the entries. // mVarCheckLibAddressPointer[mVarCheckLibAddressPointerCount++] = (VOID **)&mVarCheckHandlerTable; for (Index = 0; Index < mNumberOfVarCheckHandler; Index++) { mVarCheckLibAddressPointer[mVarCheckLibAddressPointerCount++] = (VOID **)&mVarCheckHandlerTable[Index]; } // // Cover VarCheckVariable and the entries. // mVarCheckLibAddressPointer[mVarCheckLibAddressPointerCount++] = (VOID **)&mVarCheckVariableTable; for (Index = 0; Index < mNumberOfVarCheckVariable; Index++) { mVarCheckLibAddressPointer[mVarCheckLibAddressPointerCount++] = (VOID **)&mVarCheckVariableTable[Index]; } ASSERT (mVarCheckLibAddressPointerCount == TotalCount); mVarCheckLibAddressPointerMaxCount = mVarCheckLibAddressPointerCount; } *AddressPointerCount = mVarCheckLibAddressPointerCount; return mVarCheckLibAddressPointer; } /** Register address pointer. The AddressPointer may be returned by VarCheckLibInitializeAtEndOfDxe(). @param[in] AddressPointer Address pointer. @retval EFI_SUCCESS The address pointer was registered successfully. @retval EFI_INVALID_PARAMETER AddressPointer is NULL. @retval EFI_ACCESS_DENIED EFI_END_OF_DXE_EVENT_GROUP_GUID or EFI_EVENT_GROUP_READY_TO_BOOT has already been signaled. @retval EFI_OUT_OF_RESOURCES There is not enough resource for the address pointer register request. **/ EFI_STATUS EFIAPI VarCheckLibRegisterAddressPointer ( IN VOID **AddressPointer ) { EFI_STATUS Status; if (AddressPointer == NULL) { return EFI_INVALID_PARAMETER; } if (mVarCheckLibEndOfDxe) { return EFI_ACCESS_DENIED; } Status = VarCheckAddTableEntry ( (UINTN **)&mVarCheckLibAddressPointer, &mVarCheckLibAddressPointerMaxCount, &mVarCheckLibAddressPointerCount, (UINTN)AddressPointer ); DEBUG ((DEBUG_INFO, "VarCheckLibRegisterAddressPointer - 0x%x %r\n", AddressPointer, Status)); return Status; } /** Register SetVariable check handler. @param[in] Handler Pointer to check handler. @retval EFI_SUCCESS The SetVariable check handler was registered successfully. @retval EFI_INVALID_PARAMETER Handler is NULL. @retval EFI_ACCESS_DENIED EFI_END_OF_DXE_EVENT_GROUP_GUID or EFI_EVENT_GROUP_READY_TO_BOOT has already been signaled. @retval EFI_OUT_OF_RESOURCES There is not enough resource for the SetVariable check handler register request. @retval EFI_UNSUPPORTED This interface is not implemented. For example, it is unsupported in VarCheck protocol if both VarCheck and SmmVarCheck protocols are present. **/ EFI_STATUS EFIAPI VarCheckLibRegisterSetVariableCheckHandler ( IN VAR_CHECK_SET_VARIABLE_CHECK_HANDLER Handler ) { EFI_STATUS Status; if (Handler == NULL) { return EFI_INVALID_PARAMETER; } if (mVarCheckLibEndOfDxe) { return EFI_ACCESS_DENIED; } Status = VarCheckAddTableEntry ( (UINTN **)&mVarCheckHandlerTable, &mMaxNumberOfVarCheckHandler, &mNumberOfVarCheckHandler, (UINTN)Handler ); DEBUG ((DEBUG_INFO, "VarCheckLibRegisterSetVariableCheckHandler - 0x%x %r\n", Handler, Status)); return Status; } /** Variable property set. @param[in] Name Pointer to the variable name. @param[in] Guid Pointer to the vendor GUID. @param[in] VariableProperty Pointer to the input variable property. @retval EFI_SUCCESS The property of variable specified by the Name and Guid was set successfully. @retval EFI_INVALID_PARAMETER Name, Guid or VariableProperty is NULL, or Name is an empty string, or the fields of VariableProperty are not valid. @retval EFI_ACCESS_DENIED EFI_END_OF_DXE_EVENT_GROUP_GUID or EFI_EVENT_GROUP_READY_TO_BOOT has already been signaled. @retval EFI_OUT_OF_RESOURCES There is not enough resource for the variable property set request. **/ EFI_STATUS EFIAPI VarCheckLibVariablePropertySet ( IN CHAR16 *Name, IN EFI_GUID *Guid, IN VAR_CHECK_VARIABLE_PROPERTY *VariableProperty ) { EFI_STATUS Status; VAR_CHECK_VARIABLE_ENTRY *Entry; CHAR16 *VariableName; VAR_CHECK_VARIABLE_PROPERTY *Property; if ((Name == NULL) || (Name[0] == 0) || (Guid == NULL)) { return EFI_INVALID_PARAMETER; } if (VariableProperty == NULL) { return EFI_INVALID_PARAMETER; } if (VariableProperty->Revision != VAR_CHECK_VARIABLE_PROPERTY_REVISION) { return EFI_INVALID_PARAMETER; } if (mVarCheckLibEndOfDxe) { return EFI_ACCESS_DENIED; } Status = EFI_SUCCESS; // // Get the pointer of property data for set. // Property = VariablePropertyGetFunction (Name, Guid, FALSE); if (Property != NULL) { CopyMem (Property, VariableProperty, sizeof (*VariableProperty)); } else { Entry = AllocateRuntimeZeroPool (sizeof (*Entry) + StrSize (Name)); if (Entry == NULL) { return EFI_OUT_OF_RESOURCES; } VariableName = (CHAR16 *)((UINTN)Entry + sizeof (*Entry)); StrCpyS (VariableName, StrSize (Name)/sizeof (CHAR16), Name); CopyGuid (&Entry->Guid, Guid); CopyMem (&Entry->VariableProperty, VariableProperty, sizeof (*VariableProperty)); Status = VarCheckAddTableEntry ( (UINTN **)&mVarCheckVariableTable, &mMaxNumberOfVarCheckVariable, &mNumberOfVarCheckVariable, (UINTN)Entry ); if (EFI_ERROR (Status)) { FreePool (Entry); } } return Status; } /** Variable property get. @param[in] Name Pointer to the variable name. @param[in] Guid Pointer to the vendor GUID. @param[out] VariableProperty Pointer to the output variable property. @retval EFI_SUCCESS The property of variable specified by the Name and Guid was got successfully. @retval EFI_INVALID_PARAMETER Name, Guid or VariableProperty is NULL, or Name is an empty string. @retval EFI_NOT_FOUND The property of variable specified by the Name and Guid was not found. **/ EFI_STATUS EFIAPI VarCheckLibVariablePropertyGet ( IN CHAR16 *Name, IN EFI_GUID *Guid, OUT VAR_CHECK_VARIABLE_PROPERTY *VariableProperty ) { VAR_CHECK_VARIABLE_PROPERTY *Property; if ((Name == NULL) || (Name[0] == 0) || (Guid == NULL)) { return EFI_INVALID_PARAMETER; } if (VariableProperty == NULL) { return EFI_INVALID_PARAMETER; } Property = VariablePropertyGetFunction (Name, Guid, TRUE); // // Also check the property revision before using the property data. // There is no property set to this variable(wildcard name) // if the revision is not VAR_CHECK_VARIABLE_PROPERTY_REVISION. // if ((Property != NULL) && (Property->Revision == VAR_CHECK_VARIABLE_PROPERTY_REVISION)) { CopyMem (VariableProperty, Property, sizeof (*VariableProperty)); return EFI_SUCCESS; } return EFI_NOT_FOUND; } /** SetVariable check. @param[in] VariableName Name of Variable to set. @param[in] VendorGuid Variable vendor GUID. @param[in] Attributes Attribute value of the variable. @param[in] DataSize Size of Data to set. @param[in] Data Data pointer. @param[in] RequestSource Request source. @retval EFI_SUCCESS The SetVariable check result was success. @retval EFI_INVALID_PARAMETER An invalid combination of attribute bits, name, GUID, DataSize and Data value was supplied. @retval EFI_WRITE_PROTECTED The variable in question is read-only. @retval Others The other return status from check handler. **/ EFI_STATUS EFIAPI VarCheckLibSetVariableCheck ( IN CHAR16 *VariableName, IN EFI_GUID *VendorGuid, IN UINT32 Attributes, IN UINTN DataSize, IN VOID *Data, IN VAR_CHECK_REQUEST_SOURCE RequestSource ) { EFI_STATUS Status; UINTN Index; VAR_CHECK_VARIABLE_PROPERTY *Property; if (!mVarCheckLibEndOfDxe) { // // Only do check after End Of Dxe. // return EFI_SUCCESS; } Property = VariablePropertyGetFunction (VariableName, VendorGuid, TRUE); // // Also check the property revision before using the property data. // There is no property set to this variable(wildcard name) // if the revision is not VAR_CHECK_VARIABLE_PROPERTY_REVISION. // if ((Property != NULL) && (Property->Revision == VAR_CHECK_VARIABLE_PROPERTY_REVISION)) { if ((RequestSource != VarCheckFromTrusted) && ((Property->Property & VAR_CHECK_VARIABLE_PROPERTY_READ_ONLY) != 0)) { DEBUG ((DEBUG_INFO, "Variable Check ReadOnly variable fail %r - %g:%s\n", EFI_WRITE_PROTECTED, VendorGuid, VariableName)); return EFI_WRITE_PROTECTED; } if (!((((Attributes & EFI_VARIABLE_APPEND_WRITE) == 0) && (DataSize == 0)) || (Attributes == 0))) { // // Not to delete variable. // if ((Property->Attributes != 0) && ((Attributes & (~EFI_VARIABLE_APPEND_WRITE)) != Property->Attributes)) { DEBUG ((DEBUG_INFO, "Variable Check Attributes(0x%08x to 0x%08x) fail %r - %g:%s\n", Property->Attributes, Attributes, EFI_INVALID_PARAMETER, VendorGuid, VariableName)); return EFI_INVALID_PARAMETER; } if (DataSize != 0) { if ((DataSize < Property->MinSize) || (DataSize > Property->MaxSize)) { DEBUG ((DEBUG_INFO, "Variable Check DataSize fail(0x%x not in 0x%x - 0x%x) %r - %g:%s\n", DataSize, Property->MinSize, Property->MaxSize, EFI_INVALID_PARAMETER, VendorGuid, VariableName)); return EFI_INVALID_PARAMETER; } } } } for (Index = 0; Index < mNumberOfVarCheckHandler; Index++) { Status = mVarCheckHandlerTable[Index]( VariableName, VendorGuid, Attributes, DataSize, Data ); if ((Status == EFI_WRITE_PROTECTED) && (RequestSource == VarCheckFromTrusted)) { // // If RequestSource is trusted, then allow variable to be set even if it // is write protected. // continue; } if (EFI_ERROR (Status)) { DEBUG ((DEBUG_INFO, "Variable Check handler fail %r - %g:%s\n", Status, VendorGuid, VariableName)); return Status; } } return EFI_SUCCESS; }