* [PATCH v1 2/9] MdeModulePkg: Define the VariablePolicyLib [not found] <20200410183802.21192-1-michael.kubacki@outlook.com> @ 2020-04-10 18:37 ` Michael Kubacki 2020-04-22 9:14 ` [edk2-devel] " Guomin Jiang 2020-04-10 18:37 ` [PATCH v1 3/9] MdeModulePkg: Define the VariablePolicyHelperLib Michael Kubacki ` (6 subsequent siblings) 7 siblings, 1 reply; 10+ messages in thread From: Michael Kubacki @ 2020-04-10 18:37 UTC (permalink / raw) To: devel; +Cc: Jian J Wang, Hao A Wu, Liming Gao From: Bret Barkelew <brbarkel@microsoft.com> https://bugzilla.tianocore.org/show_bug.cgi?id=2522 VariablePolicy is an updated interface to replace VarLock and VarCheckProtocol. Add the VariablePolicyLib library that implements the portable business logic for the VariablePolicy engine. Also add host-based CI test cases for the lib. Cc: Jian J Wang <jian.j.wang@intel.com> Cc: Hao A Wu <hao.a.wu@intel.com> Cc: Liming Gao <liming.gao@intel.com> Signed-off-by: Bret Barkelew <brbarkel@microsoft.com> Signed-off-by: Michael Kubacki <michael.kubacki@microsoft.com> --- MdeModulePkg/Library/VariablePolicyLib/VariablePolicyLib.c | 773 +++++++ MdeModulePkg/Library/VariablePolicyLib/VariablePolicyUnitTest/VariablePolicyUnitTest.c | 2285 ++++++++++++++++++++ MdeModulePkg/Include/Library/VariablePolicyLib.h | 206 ++ MdeModulePkg/Library/VariablePolicyLib/VariablePolicyLib.inf | 38 + MdeModulePkg/Library/VariablePolicyLib/VariablePolicyLib.uni | 12 + MdeModulePkg/Library/VariablePolicyLib/VariablePolicyUnitTest/VariablePolicyUnitTest.inf | 41 + MdeModulePkg/MdeModulePkg.dec | 3 + MdeModulePkg/MdeModulePkg.dsc | 3 + MdeModulePkg/Test/MdeModulePkgHostTest.dsc | 8 + 9 files changed, 3369 insertions(+) diff --git a/MdeModulePkg/Library/VariablePolicyLib/VariablePolicyLib.c b/MdeModulePkg/Library/VariablePolicyLib/VariablePolicyLib.c new file mode 100644 index 000000000000..52e025f2d0cf --- /dev/null +++ b/MdeModulePkg/Library/VariablePolicyLib/VariablePolicyLib.c @@ -0,0 +1,773 @@ +/** @file -- VariablePolicyLib.c +Business logic for Variable Policy enforcement. + +Copyright (c) Microsoft Corporation. +SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include <Uefi.h> + +#include <Library/SafeIntLib.h> +#include <Library/MemoryAllocationLib.h> +#include <Library/BaseMemoryLib.h> +#include <Protocol/VariablePolicy.h> +#include <Library/VariablePolicyLib.h> + +#include <Library/DebugLib.h> + +// IMPORTANT NOTE: This library is currently rife with multiple return statements +// for error handling. A refactor should remove these at some point. + +// +// This library was designed with advanced unit-test features. +// This define handles the configuration. +#ifdef INTERNAL_UNIT_TEST +#undef STATIC +#define STATIC // Nothing... +#endif + +// An abstracted GetVariable interface that enables configuration regardless of the environment. +STATIC EFI_GET_VARIABLE mGetVariableHelper = NULL; + +// Master switch to lock this entire interface. Does not stop enforcement, +// just prevents the configuration from being changed for the rest of the boot. +STATIC BOOLEAN mInterfaceLocked = FALSE; + +// Master switch to disable the entire interface for a single boot. +// This will disable all policy enforcement for the duration of the boot. +STATIC BOOLEAN mProtectionDisabled = FALSE; + +// Table to hold all the current policies. +STATIC UINT8 *mPolicyTable = NULL; +STATIC UINT32 mCurrentTableSize = 0; +STATIC UINT32 mCurrentTableUsage = 0; +STATIC UINT32 mCurrentTableCount = 0; + +#define POLICY_TABLE_STEP_SIZE 0x1000 + +// NOTE: DO NOT USE THESE MACROS on any structure that has not been validated. +// Current table data has already been sanitized. +#define GET_NEXT_POLICY(CurPolicy) (VARIABLE_POLICY_ENTRY*)((UINT8*)CurPolicy + CurPolicy->Size) +#define GET_POLICY_NAME(CurPolicy) (CHAR16*)((UINTN)CurPolicy + CurPolicy->OffsetToName) + +#define MATCH_PRIORITY_EXACT 0 +#define MATCH_PRIORITY_MAX MATCH_PRIORITY_EXACT +#define MATCH_PRIORITY_MIN MAX_UINT8 + + +/** + This helper function determines whether the structure of an incoming policy + is valid and internally consistent. + + @param[in] NewPolicy Pointer to the incoming policy structure. + + @retval TRUE + @retval FALSE Pointer is NULL, size is wrong, strings are empty, or + substructures overlap. + +**/ +STATIC +BOOLEAN +IsValidVariablePolicyStructure ( + IN CONST VARIABLE_POLICY_ENTRY *NewPolicy + ) +{ + EFI_STATUS Status; + UINTN EntryEnd; + CHAR16 *CheckChar; + UINTN WildcardCount; + + // Sanitize some quick values. + if (NewPolicy == NULL || NewPolicy->Size == 0 || + // Structure size should be at least as long as the minumum structure and a NULL string. + NewPolicy->Size < sizeof(VARIABLE_POLICY_ENTRY) || + // Check for the known revision. + NewPolicy->Version != VARIABLE_POLICY_ENTRY_REVISION) { + return FALSE; + } + + // Calculate the theoretical end of the structure and make sure + // that the structure can fit in memory. + Status = SafeUintnAdd( (UINTN)NewPolicy, NewPolicy->Size, &EntryEnd ); + if (EFI_ERROR( Status )) { + return FALSE; + } + + // Check for a valid Max Size. + if (NewPolicy->MaxSize == 0) { + return FALSE; + } + + // Check for the valid list of lock policies. + if (NewPolicy->LockPolicyType != VARIABLE_POLICY_TYPE_NO_LOCK && + NewPolicy->LockPolicyType != VARIABLE_POLICY_TYPE_LOCK_NOW && + NewPolicy->LockPolicyType != VARIABLE_POLICY_TYPE_LOCK_ON_CREATE && + NewPolicy->LockPolicyType != VARIABLE_POLICY_TYPE_LOCK_ON_VAR_STATE) + { + return FALSE; + } + + // If the policy type is VARIABLE_POLICY_TYPE_LOCK_ON_VAR_STATE, make sure that the matching state variable Name + // terminates before the OffsetToName for the matching policy variable Name. + if (NewPolicy->LockPolicyType == VARIABLE_POLICY_TYPE_LOCK_ON_VAR_STATE) { + // Adjust CheckChar to the offset of the LockPolicy->Name. + Status = SafeUintnAdd( (UINTN)NewPolicy + sizeof(VARIABLE_POLICY_ENTRY), + sizeof(VARIABLE_LOCK_ON_VAR_STATE_POLICY), + (UINTN*)&CheckChar ); + if (EFI_ERROR( Status ) || EntryEnd <= (UINTN)CheckChar) { + return FALSE; + } + while (*CheckChar != CHAR_NULL) { + if (EntryEnd <= (UINTN)CheckChar) { + return FALSE; + } + CheckChar++; + } + // At this point we should have either exeeded the structure or be pointing at the last char in LockPolicy->Name. + // We should check to make sure that the policy Name comes immediately after this charcter. + if ((UINTN)++CheckChar != (UINTN)NewPolicy + NewPolicy->OffsetToName) { + return FALSE; + } + } + // If the policy type is any other value, make sure that the LockPolicy structure has a zero length. + else { + if (NewPolicy->OffsetToName != sizeof(VARIABLE_POLICY_ENTRY)) { + return FALSE; + } + } + + // Check to make sure that the name has a terminating character + // before the end of the structure. + // We've already checked that the name is within the bounds of the structure. + if (NewPolicy->Size != NewPolicy->OffsetToName) { + CheckChar = (CHAR16*)((UINTN)NewPolicy + NewPolicy->OffsetToName); + WildcardCount = 0; + while (*CheckChar != CHAR_NULL) { + // Make sure there aren't excessive wildcards. + if (*CheckChar == '#') { + WildcardCount++; + if (WildcardCount > MATCH_PRIORITY_MIN) { + return FALSE; + } + } + // Make sure you're still within the bounds of the policy structure. + if (EntryEnd <= (UINTN)CheckChar) { + return FALSE; + } + CheckChar++; + } + + // Finally, we should be pointed at the very last character in Name, so we should be right + // up against the end of the structure. + if ((UINTN)++CheckChar != EntryEnd) { + return FALSE; + } + } + + return TRUE; +} + + +/** + This helper function evaluates a policy and determines whether it matches the target + variable. If matched, will also return a value corresponding to the priority of the match. + + The rules for "best match" are listed in the Variable Policy Spec. + Perfect name matches will return 0. + Single wildcard characters will return the number of wildcard characters. + Full namespaces will return MAX_UINT8. + + @param[in] EvalEntry Pointer to the policy entry being evaluated. + @param[in] VariableName Same as EFI_SET_VARIABLE. + @param[in] VendorGuid Same as EFI_SET_VARIABLE. + @param[out] MatchPriority [Optional] On finding a match, this value contains the priority of the match. + Lower number == higher priority. Only valid if a match found. + + @retval TRUE Current entry matches the target variable. + @retval FALSE Current entry does not match at all. + +**/ +STATIC +BOOLEAN +EvaluatePolicyMatch ( + IN CONST VARIABLE_POLICY_ENTRY *EvalEntry, + IN CONST CHAR16 *VariableName, + IN CONST EFI_GUID *VendorGuid, + OUT UINT8 *MatchPriority OPTIONAL + ) +{ + BOOLEAN Result = FALSE; + CHAR16 *PolicyName; + UINT8 CalculatedPriority = MATCH_PRIORITY_EXACT; + UINTN Index; + + // Step 1: If the GUID doesn't match, we're done. No need to evaluate anything else. + if (!CompareGuid( &EvalEntry->Namespace, VendorGuid )) { + goto Exit; + } + + // If the GUID matches, check to see whether there is a Name associated + // with the policy. If not, this policy matches the entire namespace. + // Missing Name is indicated by size being equal to name. + if (EvalEntry->Size == EvalEntry->OffsetToName) { + CalculatedPriority = MATCH_PRIORITY_MIN; + Result = TRUE; + goto Exit; + } + + // Now that we know the name exists, get it. + PolicyName = GET_POLICY_NAME( EvalEntry ); + + // Evaluate the name against the policy name and check for a match. + // Account for any wildcards. + Index = 0; + Result = TRUE; + // Keep going until the end of both strings. + while (PolicyName[Index] != CHAR_NULL || VariableName[Index] != CHAR_NULL) { + // If we don't have a match... + if (PolicyName[Index] != VariableName[Index] || PolicyName[Index] == '#') { + // If this is a numerical wildcard, we can consider + // it a match if we alter the priority. + if (PolicyName[Index] == L'#' && + (L'0' <= VariableName[Index] && VariableName[Index] <= L'9')) { + if (CalculatedPriority < MATCH_PRIORITY_MIN) { + CalculatedPriority++; + } + } + // Otherwise, not a match. + else { + Result = FALSE; + goto Exit; + } + } + Index++; + } + +Exit: + if (Result && MatchPriority != NULL) { + *MatchPriority = CalculatedPriority; + } + return Result; +} + + +/** + This helper function walks the current policy table and returns a pointer + to the best match, if any are found. Leverages EvaluatePolicyMatch() to + determine "best". + + @param[in] VariableName Same as EFI_SET_VARIABLE. + @param[in] VendorGuid Same as EFI_SET_VARIABLE. + @param[out] ReturnPriority [Optional] If pointer is provided, return the + priority of the match. Same as EvaluatePolicyMatch(). + Only valid if a match is returned. + + @retval VARIABLE_POLICY_ENTRY* Best match that was found. + @retval NULL No match was found. + +**/ +STATIC +VARIABLE_POLICY_ENTRY* +GetBestPolicyMatch ( + IN CONST CHAR16 *VariableName, + IN CONST EFI_GUID *VendorGuid, + OUT UINT8 *ReturnPriority OPTIONAL + ) +{ + VARIABLE_POLICY_ENTRY *BestResult = NULL; + VARIABLE_POLICY_ENTRY *CurrentEntry; + UINT8 MatchPriority; + UINT8 CurrentPriority; + UINTN Index; + + // Walk all entries in the table, looking for matches. + CurrentEntry = (VARIABLE_POLICY_ENTRY*)mPolicyTable; + for (Index = 0; Index < mCurrentTableCount; Index++) { + // Check for a match. + if (EvaluatePolicyMatch( CurrentEntry, VariableName, VendorGuid, &CurrentPriority ) == TRUE) { + // If match is better, take it. + if (BestResult == NULL || CurrentPriority < MatchPriority) { + BestResult = CurrentEntry; + MatchPriority = CurrentPriority; + } + + // If you've hit the highest-priority match, can exit now. + if (MatchPriority == 0) { + break; + } + } + + // If we're still in the loop, move to the next entry. + CurrentEntry = GET_NEXT_POLICY( CurrentEntry ); + } + + // If a return priority was requested, return it. + if (ReturnPriority != NULL) { + *ReturnPriority = MatchPriority; + } + + return BestResult; +} + + +/** + This API function validates and registers a new policy with + the policy enforcement engine. + + @param[in] NewPolicy Pointer to the incoming policy structure. + + @retval EFI_SUCCESS + @retval EFI_INVALID_PARAMETER NewPolicy is NULL or is internally inconsistent. + @retval EFI_ALREADY_STARTED An identical matching policy already exists. + @retval EFI_WRITE_PROTECTED The interface has been locked until the next reboot. + @retval EFI_UNSUPPORTED Policy enforcement has been disabled. No reason to add more policies. + @retval EFI_ABORTED A calculation error has prevented this function from completing. + @retval EFI_OUT_OF_RESOURCES Cannot grow the table to hold any more policies. + @retval EFI_NOT_READY Library has not yet been initialized. + +**/ +EFI_STATUS +EFIAPI +RegisterVariablePolicy ( + IN CONST VARIABLE_POLICY_ENTRY *NewPolicy + ) +{ + EFI_STATUS Status; + VARIABLE_POLICY_ENTRY *MatchPolicy; + UINT8 MatchPriority; + UINT32 NewSize; + UINT8 *NewTable; + + if (!IsVariablePolicyLibInitialized()) { + return EFI_NOT_READY; + } + if (mInterfaceLocked) { + return EFI_WRITE_PROTECTED; + } + + if (!IsValidVariablePolicyStructure( NewPolicy )) { + return EFI_INVALID_PARAMETER; + } + + // Check to see whether an exact matching policy already exists. + MatchPolicy = GetBestPolicyMatch( GET_POLICY_NAME( NewPolicy ), + &NewPolicy->Namespace, + &MatchPriority ); + if (MatchPolicy != NULL && MatchPriority == MATCH_PRIORITY_EXACT) { + return EFI_ALREADY_STARTED; + } + + // If none exists, create it. + // If we need more space, allocate that now. + Status = SafeUint32Add( mCurrentTableUsage, NewPolicy->Size, &NewSize ); + if (EFI_ERROR( Status )) { + return EFI_ABORTED; + } + if (NewSize > mCurrentTableSize) { + // Use NewSize to calculate the new table size in units of POLICY_TABLE_STEP_SIZE. + NewSize = (NewSize % POLICY_TABLE_STEP_SIZE) > 0 ? + (NewSize / POLICY_TABLE_STEP_SIZE) + 1 : + (NewSize / POLICY_TABLE_STEP_SIZE); + // Calculate the new table size in absolute bytes. + Status = SafeUint32Mult( NewSize, POLICY_TABLE_STEP_SIZE, &NewSize ); + if (EFI_ERROR( Status )) { + return EFI_ABORTED; + } + + // Reallocate and copy the table. + NewTable = AllocatePool( NewSize ); + if (NewTable == NULL) { + return EFI_OUT_OF_RESOURCES; + } + CopyMem( NewTable, mPolicyTable, mCurrentTableUsage ); + mCurrentTableSize = NewSize; + if (mPolicyTable != NULL) { + FreePool( mPolicyTable ); + } + mPolicyTable = NewTable; + } + // Copy the policy into the table. + CopyMem( mPolicyTable + mCurrentTableUsage, NewPolicy, NewPolicy->Size ); + mCurrentTableUsage += NewPolicy->Size; + mCurrentTableCount += 1; + + // We're done here. + + return EFI_SUCCESS; +} + + +/** + This API function checks to see whether the parameters to SetVariable would + be allowed according to the current variable policies. + + @param[in] VariableName Same as EFI_SET_VARIABLE. + @param[in] VendorGuid Same as EFI_SET_VARIABLE. + @param[in] Attributes Same as EFI_SET_VARIABLE. + @param[in] DataSize Same as EFI_SET_VARIABLE. + @param[in] Data Same as EFI_SET_VARIABLE. + + @retval EFI_SUCCESS A matching policy allows this update. + @retval EFI_SUCCESS There are currently no policies that restrict this update. + @retval EFI_SUCCESS The protections have been disable until the next reboot. + @retval EFI_WRITE_PROTECTED Variable is currently locked. + @retval EFI_INVALID_PARAMETER Attributes or size are invalid. + @retval EFI_ABORTED A lock policy exists, but an error prevented evaluation. + @retval EFI_NOT_READY Library has not been initialized. + +**/ +EFI_STATUS +EFIAPI +ValidateSetVariable ( + IN CHAR16 *VariableName, + IN EFI_GUID *VendorGuid, + IN UINT32 Attributes, + IN UINTN DataSize, + IN VOID *Data + ) +{ + BOOLEAN IsDel; + VARIABLE_POLICY_ENTRY *ActivePolicy; + EFI_STATUS Status; + EFI_STATUS ReturnStatus = EFI_SUCCESS; + VARIABLE_LOCK_ON_VAR_STATE_POLICY *StateVarPolicy; + CHAR16 *StateVarName; + UINTN StateVarSize; + UINT8 StateVar; + + if (!IsVariablePolicyLibInitialized()) { + ReturnStatus = EFI_NOT_READY; + goto Exit; + } + + // Bail if the protections are currently disabled. + if (mProtectionDisabled == TRUE) { + ReturnStatus = EFI_SUCCESS; + goto Exit; + } + + // Determine whether this is a delete operation. + // If so, it will affect which tests are applied. + if ((DataSize == 0) && ((Attributes & EFI_VARIABLE_APPEND_WRITE) == 0)) { + IsDel = TRUE; + } + else { + IsDel = FALSE; + } + + // Find an active policy if one exists. + ActivePolicy = GetBestPolicyMatch( VariableName, VendorGuid, NULL ); + + // If we have an active policy, check it against the incoming data. + if (ActivePolicy != NULL) { + // + // Only enforce size and attribute constraints when updating data, not deleting. + if (!IsDel) { + // Check for size constraints. + if ((ActivePolicy->MinSize > 0 && DataSize < ActivePolicy->MinSize) || + (ActivePolicy->MaxSize > 0 && DataSize > ActivePolicy->MaxSize)) { + ReturnStatus = EFI_INVALID_PARAMETER; + DEBUG(( DEBUG_VERBOSE, "%a - Bad Size. 0x%X <> 0x%X-0x%X\n", __FUNCTION__, + DataSize, ActivePolicy->MinSize, ActivePolicy->MaxSize )); + goto Exit; + } + + // Check for attribute constraints. + if ((ActivePolicy->AttributesMustHave & Attributes) != ActivePolicy->AttributesMustHave || + (ActivePolicy->AttributesCantHave & Attributes) != 0) { + ReturnStatus = EFI_INVALID_PARAMETER; + DEBUG(( DEBUG_VERBOSE, "%a - Bad Attributes. 0x%X <> 0x%X:0x%X\n", __FUNCTION__, + Attributes, ActivePolicy->AttributesMustHave, ActivePolicy->AttributesCantHave )); + goto Exit; + } + } + + // + // Lock policy check. + // + // Check for immediate lock. + if (ActivePolicy->LockPolicyType == VARIABLE_POLICY_TYPE_LOCK_NOW) { + ReturnStatus = EFI_WRITE_PROTECTED; + goto Exit; + } + // Check for lock on create. + else if (ActivePolicy->LockPolicyType == VARIABLE_POLICY_TYPE_LOCK_ON_CREATE) { + StateVarSize = 0; + Status = mGetVariableHelper( VariableName, + VendorGuid, + NULL, + &StateVarSize, + NULL ); + if (Status == EFI_BUFFER_TOO_SMALL) { + ReturnStatus = EFI_WRITE_PROTECTED; + goto Exit; + } + } + // Check for lock on state variable. + else if (ActivePolicy->LockPolicyType == VARIABLE_POLICY_TYPE_LOCK_ON_VAR_STATE) { + StateVarPolicy = (VARIABLE_LOCK_ON_VAR_STATE_POLICY*)((UINT8*)ActivePolicy + sizeof(VARIABLE_POLICY_ENTRY)); + StateVarName = (CHAR16*)((UINT8*)StateVarPolicy + sizeof(VARIABLE_LOCK_ON_VAR_STATE_POLICY)); + StateVarSize = sizeof(StateVar); + Status = mGetVariableHelper( StateVarName, + &StateVarPolicy->Namespace, + NULL, + &StateVarSize, + &StateVar ); + + // If the variable was found, check the state. If matched, this variable is locked. + if (!EFI_ERROR( Status )) { + if (StateVar == StateVarPolicy->Value) { + ReturnStatus = EFI_WRITE_PROTECTED; + goto Exit; + } + } + // EFI_NOT_FOUND and EFI_BUFFER_TOO_SMALL indicate that the state doesn't match. + else if (Status != EFI_NOT_FOUND && Status != EFI_BUFFER_TOO_SMALL) { + // We don't know what happened, but it isn't good. + ReturnStatus = EFI_ABORTED; + goto Exit; + } + } + } + +Exit: + DEBUG(( DEBUG_VERBOSE, "%a - Variable (%g:%s) returning %r.\n", __FUNCTION__, VendorGuid, VariableName, ReturnStatus )); + return ReturnStatus; +} + + +/** + This API function disables the variable policy enforcement. If it's + already been called once, will return EFI_ALREADY_STARTED. + + @retval EFI_SUCCESS + @retval EFI_ALREADY_STARTED Has already been called once this boot. + @retval EFI_WRITE_PROTECTED Interface has been locked until reboot. + @retval EFI_NOT_READY Library has not yet been initialized. + +**/ +EFI_STATUS +EFIAPI +DisableVariablePolicy ( + VOID + ) +{ + if (!IsVariablePolicyLibInitialized()) { + return EFI_NOT_READY; + } + if (mProtectionDisabled) { + return EFI_ALREADY_STARTED; + } + if (mInterfaceLocked) { + return EFI_WRITE_PROTECTED; + } + mProtectionDisabled = TRUE; + return EFI_SUCCESS; +} + + +/** + This API function will dump the entire contents of the variable policy table. + + Similar to GetVariable, the first call can be made with a 0 size and it will return + the size of the buffer required to hold the entire table. + + @param[out] Policy Pointer to the policy buffer. Can be NULL if Size is 0. + @param[in,out] Size On input, the size of the output buffer. On output, the size + of the data returned. + + @retval EFI_SUCCESS Policy data is in the output buffer and Size has been updated. + @retval EFI_INVALID_PARAMETER Size is NULL, or Size is non-zero and Policy is NULL. + @retval EFI_BUFFER_TOO_SMALL Size is insufficient to hold policy. Size updated with required size. + @retval EFI_NOT_READY Library has not yet been initialized. + +**/ +EFI_STATUS +EFIAPI +DumpVariablePolicy ( + OUT UINT8 *Policy, + IN OUT UINT32 *Size + ) +{ + if (!IsVariablePolicyLibInitialized()) { + return EFI_NOT_READY; + } + + // Check the parameters. + if (Size == NULL || (*Size > 0 && Policy == NULL)) { + return EFI_INVALID_PARAMETER; + } + + // Make sure the size is sufficient to hold the policy table. + if (*Size < mCurrentTableUsage) { + *Size = mCurrentTableUsage; + return EFI_BUFFER_TOO_SMALL; + } + + // If we're still here, copy the table and bounce. + CopyMem( Policy, mPolicyTable, mCurrentTableUsage ); + *Size = mCurrentTableUsage; + + return EFI_SUCCESS; +} + + +/** + This API function returns whether or not the policy engine is + currently being enforced. + + @retval TRUE + @retval FALSE + @retval FALSE Library has not yet been initialized. + +**/ +BOOLEAN +EFIAPI +IsVariablePolicyEnabled ( + VOID + ) +{ + if (!IsVariablePolicyLibInitialized()) { + return FALSE; + } + return !mProtectionDisabled; +} + + +/** + This API function locks the interface so that no more policy updates + can be performed or changes made to the enforcement until the next boot. + + @retval EFI_SUCCESS + @retval EFI_NOT_READY Library has not yet been initialized. + +**/ +EFI_STATUS +EFIAPI +LockVariablePolicy ( + VOID + ) +{ + if (!IsVariablePolicyLibInitialized()) { + return EFI_NOT_READY; + } + if (mInterfaceLocked) { + return EFI_WRITE_PROTECTED; + } + mInterfaceLocked = TRUE; + return EFI_SUCCESS; +} + + +/** + This API function returns whether or not the policy interface is locked + for the remainder of the boot. + + @retval TRUE + @retval FALSE + @retval FALSE Library has not yet been initialized. + +**/ +BOOLEAN +EFIAPI +IsVariablePolicyInterfaceLocked ( + VOID + ) +{ + if (!IsVariablePolicyLibInitialized()) { + return FALSE; + } + return mInterfaceLocked; +} + + +/** + This helper function initializes the library and sets + up any required internal structures or handlers. + + Also registers the internal pointer for the GetVariable helper. + + @param[in] GetVariableHelper A function pointer matching the EFI_GET_VARIABLE prototype that will be used to + check policy criteria that involve the existence of other variables. + + @retval EFI_SUCCESS + @retval EFI_ALREADY_STARTED The initialize function has been called more than once without a call to + deinitialize. + +**/ +EFI_STATUS +EFIAPI +InitVariablePolicyLib ( + IN EFI_GET_VARIABLE GetVariableHelper + ) +{ + if (mGetVariableHelper != NULL) { + return EFI_ALREADY_STARTED; + } + + // Save an internal pointer to the GetVariableHelper. + mGetVariableHelper = GetVariableHelper; + + // Initialize the global state. + mInterfaceLocked = FALSE; + mProtectionDisabled = FALSE; + mPolicyTable = NULL; + mCurrentTableSize = 0; + mCurrentTableUsage = 0; + mCurrentTableCount = 0; + + return EFI_SUCCESS; +} + + +/** + This helper function returns whether or not the library is currently initialized. + + @retval TRUE + @retval FALSE + +**/ +BOOLEAN +EFIAPI +IsVariablePolicyLibInitialized ( + VOID + ) +{ + return (mGetVariableHelper != NULL); +} + + +/** + This helper function tears down the library. + + Should generally only be used for test harnesses. + + @retval EFI_SUCCESS + @retval EFI_NOT_READY Deinitialize was called without first calling initialize. + +**/ +EFI_STATUS +EFIAPI +DeinitVariablePolicyLib ( + VOID + ) +{ + if (mGetVariableHelper == NULL) { + return EFI_NOT_READY; + } + + mGetVariableHelper = NULL; + mInterfaceLocked = FALSE; + mProtectionDisabled = FALSE; + mCurrentTableSize = 0; + mCurrentTableUsage = 0; + mCurrentTableCount = 0; + + if (mPolicyTable != NULL) { + FreePool( mPolicyTable ); + mPolicyTable = NULL; + } + + return EFI_SUCCESS; +} diff --git a/MdeModulePkg/Library/VariablePolicyLib/VariablePolicyUnitTest/VariablePolicyUnitTest.c b/MdeModulePkg/Library/VariablePolicyLib/VariablePolicyUnitTest/VariablePolicyUnitTest.c new file mode 100644 index 000000000000..3214bff09091 --- /dev/null +++ b/MdeModulePkg/Library/VariablePolicyLib/VariablePolicyUnitTest/VariablePolicyUnitTest.c @@ -0,0 +1,2285 @@ +/** @file -- VariablePolicyUnitTest.c +UnitTest for... +Business logic for Variable Policy enforcement. + +Copyright (c) Microsoft Corporation. +SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include <stdio.h> +#include <string.h> +#include <stdarg.h> +#include <stddef.h> +#include <setjmp.h> +#include <cmocka.h> + +#include <Uefi.h> +#include <Library/PrintLib.h> +#include <Library/DebugLib.h> +#include <Library/UnitTestLib.h> +#include <Library/BaseMemoryLib.h> +#include <Library/MemoryAllocationLib.h> +#include <Library/BaseLib.h> + +#include <Guid/VariableFormat.h> + +#include <Protocol/VariablePolicy.h> +#include <Library/VariablePolicyLib.h> + +// MU_CHANGE - Turn this off for now. Try to turn it back on with extra build options. +// #ifndef INTERNAL_UNIT_TEST +// #error Make sure to build thie with INTERNAL_UNIT_TEST enabled! Otherwise, some important tests may be skipped! +// #endif + + +#define UNIT_TEST_NAME "UEFI Variable Policy UnitTest" +#define UNIT_TEST_VERSION "0.5" + +///=== TEST DATA ================================================================================== + +#pragma pack(push, 1) +typedef struct _SIMPLE_VARIABLE_POLICY_ENTRY { + VARIABLE_POLICY_ENTRY Header; + CHAR16 Name[]; +} SIMPLE_VARIABLE_POLICY_ENTRY; +#define EXPANDED_VARIABLE_POLICY_ENTRY_VAR_NAME_LENGTH 1001 // 1000 characters + terminator. +#define EXPANDED_VARIABLE_POLICY_ENTRY_VAR_NAME_SIZE (EXPANDED_VARIABLE_POLICY_ENTRY_VAR_NAME_LENGTH * sizeof(CHAR16)) +typedef struct _EXPANDED_VARIABLE_POLICY_ENTRY { + VARIABLE_POLICY_ENTRY Header; + VARIABLE_LOCK_ON_VAR_STATE_POLICY StatePolicy; + CHAR16 StateName[EXPANDED_VARIABLE_POLICY_ENTRY_VAR_NAME_LENGTH]; + CHAR16 Name[EXPANDED_VARIABLE_POLICY_ENTRY_VAR_NAME_LENGTH]; +} EXPANDED_VARIABLE_POLICY_ENTRY; +#pragma pack(pop) + +// {F955BA2D-4A2C-480C-BFD1-3CC522610592} +#define TEST_GUID_1 { 0xf955ba2d, 0x4a2c, 0x480c, { 0xbf, 0xd1, 0x3c, 0xc5, 0x22, 0x61, 0x5, 0x92 } } +EFI_GUID mTestGuid1 = TEST_GUID_1; +// {2DEA799E-5E73-43B9-870E-C945CE82AF3A} +#define TEST_GUID_2 { 0x2dea799e, 0x5e73, 0x43b9, { 0x87, 0xe, 0xc9, 0x45, 0xce, 0x82, 0xaf, 0x3a } } +EFI_GUID mTestGuid2 = TEST_GUID_2; +// {698A2BFD-A616-482D-B88C-7100BD6682A9} +#define TEST_GUID_3 { 0x698a2bfd, 0xa616, 0x482d, { 0xb8, 0x8c, 0x71, 0x0, 0xbd, 0x66, 0x82, 0xa9 } } +EFI_GUID mTestGuid3 = TEST_GUID_3; + +#define TEST_VAR_1_NAME L"TestVar1" +#define TEST_VAR_2_NAME L"TestVar2" +#define TEST_VAR_3_NAME L"TestVar3" + +#define TEST_POLICY_ATTRIBUTES_NULL 0 +#define TEST_POLICY_MIN_SIZE_NULL 0 +#define TEST_POLICY_MAX_SIZE_NULL MAX_UINT32 + +#define TEST_POLICY_MIN_SIZE_10 10 +#define TEST_POLICY_MAX_SIZE_200 200 + +#define TEST_300_HASHES_STRING L"##################################################"\ + "##################################################"\ + "##################################################"\ + "##################################################"\ + "##################################################"\ + "##################################################" + + +///=== HELPER FUNCTIONS =========================================================================== + +STATIC +BOOLEAN +InitExpVarPolicyStrings ( + EXPANDED_VARIABLE_POLICY_ENTRY *Entry, + CHAR16 *Name, OPTIONAL + CHAR16 *StateName OPTIONAL + ) +{ + UINTN NameSize; + UINTN StateNameSize; + + NameSize = Name == NULL ? 0 : StrSize( Name ); + StateNameSize = StateName == NULL ? 0 : StrSize( StateName ); + + if (NameSize > EXPANDED_VARIABLE_POLICY_ENTRY_VAR_NAME_SIZE || NameSize > MAX_UINT16 || + StateNameSize > EXPANDED_VARIABLE_POLICY_ENTRY_VAR_NAME_SIZE || StateNameSize > MAX_UINT16) { + return FALSE; + } + + Entry->Header.OffsetToName = sizeof(VARIABLE_POLICY_ENTRY); + if (StateName != NULL) { + Entry->Header.OffsetToName += (UINT16)sizeof(VARIABLE_LOCK_ON_VAR_STATE_POLICY) + (UINT16)StateNameSize; + } + Entry->Header.Size = Entry->Header.OffsetToName + (UINT16)NameSize; + + CopyMem( (UINT8*)Entry + Entry->Header.OffsetToName, Name, NameSize ); + if (StateName != NULL) { + CopyMem( (UINT8*)Entry + sizeof(VARIABLE_POLICY_ENTRY) + sizeof(VARIABLE_LOCK_ON_VAR_STATE_POLICY), StateName, StateNameSize ); + } + + return TRUE; +} + +EFI_STATUS +EFIAPI +StubGetVariableNull ( + IN CHAR16 *VariableName, + IN EFI_GUID *VendorGuid, + OUT UINT32 *Attributes, OPTIONAL + IN OUT UINTN *DataSize, + OUT VOID *Data OPTIONAL + ) +{ + UINT32 MockedAttr; + UINTN MockedDataSize; + VOID *MockedData; + EFI_STATUS MockedReturn; + + check_expected_ptr( VariableName ); + check_expected_ptr( VendorGuid ); + check_expected_ptr( DataSize ); + + MockedAttr = (UINT32)mock(); + MockedDataSize = (UINTN)mock(); + MockedData = (VOID*)mock(); + MockedReturn = (EFI_STATUS)mock(); + + if (Attributes) { + *Attributes = MockedAttr; + } + if (Data && !EFI_ERROR(MockedReturn)) { + CopyMem( Data, MockedData, MockedDataSize ); + } + + *DataSize = MockedDataSize; + + return MockedReturn; +} + +// +// Anything you think might be helpful that isn't a test itself. +// + +STATIC +UNIT_TEST_STATUS +LibInitMocked ( + IN UNIT_TEST_CONTEXT Context + ) +{ + return EFI_ERROR(InitVariablePolicyLib( StubGetVariableNull )) ? UNIT_TEST_ERROR_PREREQUISITE_NOT_MET : UNIT_TEST_PASSED; +} + +STATIC +VOID +LibCleanup ( + IN UNIT_TEST_CONTEXT Context + ) +{ + DeinitVariablePolicyLib(); +} + + +///=== TEST CASES ================================================================================= + +///===== ARCHITECTURAL SUITE ================================================== + +UNIT_TEST_STATUS +EFIAPI +ShouldBeAbleToInitAndDeinitTheLibrary ( + IN UNIT_TEST_CONTEXT Context + ) +{ + EFI_STATUS Status; + Status = InitVariablePolicyLib( StubGetVariableNull ); + UT_ASSERT_NOT_EFI_ERROR( Status ); + + UT_ASSERT_TRUE( IsVariablePolicyLibInitialized() ); + + Status = DeinitVariablePolicyLib(); + UT_ASSERT_NOT_EFI_ERROR( Status ); + + UT_ASSERT_FALSE( IsVariablePolicyLibInitialized() ); + + return UNIT_TEST_PASSED; +} + +UNIT_TEST_STATUS +EFIAPI +ShouldNotBeAbleToInitializeTheLibraryTwice ( + IN UNIT_TEST_CONTEXT Context + ) +{ + EFI_STATUS Status; + Status = InitVariablePolicyLib( StubGetVariableNull ); + UT_ASSERT_NOT_EFI_ERROR( Status ); + Status = InitVariablePolicyLib( StubGetVariableNull ); + UT_ASSERT_TRUE( EFI_ERROR( Status ) ); + return UNIT_TEST_PASSED; +} + +UNIT_TEST_STATUS +EFIAPI +ShouldFailDeinitWithoutInit ( + IN UNIT_TEST_CONTEXT Context + ) +{ + EFI_STATUS Status; + Status = DeinitVariablePolicyLib(); + UT_ASSERT_TRUE( EFI_ERROR( Status ) ); + return UNIT_TEST_PASSED; +} + +UNIT_TEST_STATUS +EFIAPI +ApiCommandsShouldNotRespondIfLibIsUninitialized ( + IN UNIT_TEST_CONTEXT Context + ) +{ + SIMPLE_VARIABLE_POLICY_ENTRY TestPolicy = { + { + VARIABLE_POLICY_ENTRY_REVISION, + sizeof(VARIABLE_POLICY_ENTRY) + sizeof(TEST_VAR_1_NAME), + sizeof(VARIABLE_POLICY_ENTRY), + TEST_GUID_1, + TEST_POLICY_MIN_SIZE_NULL, + TEST_POLICY_MAX_SIZE_NULL, + TEST_POLICY_ATTRIBUTES_NULL, + TEST_POLICY_ATTRIBUTES_NULL, + VARIABLE_POLICY_TYPE_NO_LOCK + }, + TEST_VAR_1_NAME + }; + UINT8 DummyData[8]; + UINT32 DummyDataSize = sizeof(DummyData); + + // This test should not start with an initialized library. + + // Verify that all API commands fail. + UT_ASSERT_TRUE( EFI_ERROR( LockVariablePolicy() ) ); + UT_ASSERT_TRUE( EFI_ERROR( DisableVariablePolicy() ) ); + UT_ASSERT_TRUE( EFI_ERROR( RegisterVariablePolicy( &TestPolicy.Header ) ) ); + UT_ASSERT_TRUE( EFI_ERROR( DumpVariablePolicy( DummyData, &DummyDataSize ) ) ); + UT_ASSERT_FALSE( IsVariablePolicyInterfaceLocked() ); + UT_ASSERT_FALSE( IsVariablePolicyEnabled() ); + UT_ASSERT_TRUE( EFI_ERROR( ValidateSetVariable( TEST_VAR_1_NAME, + &mTestGuid1, + VARIABLE_ATTRIBUTE_NV_BS, + sizeof(DummyData), + DummyData ) ) ); + + return UNIT_TEST_PASSED; +} + + +///===== INTERNAL FUNCTION SUITE ============================================== + +#ifdef INTERNAL_UNIT_TEST + +BOOLEAN +EvaluatePolicyMatch ( + IN CONST VARIABLE_POLICY_ENTRY *EvalEntry, + IN CONST CHAR16 *VariableName, + IN CONST EFI_GUID *VendorGuid, + OUT UINT8 *MatchPriority OPTIONAL + ); + +UNIT_TEST_STATUS +EFIAPI +PoliciesShouldMatchByNameAndGuid ( + IN UNIT_TEST_CONTEXT Context + ) +{ + SIMPLE_VARIABLE_POLICY_ENTRY MatchCheckPolicy = { + { + VARIABLE_POLICY_ENTRY_REVISION, + sizeof(VARIABLE_POLICY_ENTRY) + sizeof(TEST_VAR_1_NAME), + sizeof(VARIABLE_POLICY_ENTRY), + TEST_GUID_1, + TEST_POLICY_MIN_SIZE_NULL, + TEST_POLICY_MAX_SIZE_NULL, + TEST_POLICY_ATTRIBUTES_NULL, + TEST_POLICY_ATTRIBUTES_NULL, + VARIABLE_POLICY_TYPE_NO_LOCK + }, + TEST_VAR_1_NAME + }; + CHAR16 *CheckVar1Name = TEST_VAR_1_NAME; + CHAR16 *CheckVar2Name = TEST_VAR_2_NAME; + + // Make sure that a different name does not match. + UT_ASSERT_FALSE( EvaluatePolicyMatch( &MatchCheckPolicy.Header, CheckVar2Name, &mTestGuid1, NULL ) ); + + // Make sure that a different GUID does not match. + UT_ASSERT_FALSE( EvaluatePolicyMatch( &MatchCheckPolicy.Header, CheckVar1Name, &mTestGuid2, NULL ) ); + + // Make sure that the same name and GUID match. + UT_ASSERT_TRUE( EvaluatePolicyMatch( &MatchCheckPolicy.Header, CheckVar1Name, &mTestGuid1, NULL ) ); + + return UNIT_TEST_PASSED; +} + +UNIT_TEST_STATUS +EFIAPI +WildcardPoliciesShouldMatchDigits ( + IN UNIT_TEST_CONTEXT Context + ) +{ + SIMPLE_VARIABLE_POLICY_ENTRY MatchCheckPolicy = { + { + VARIABLE_POLICY_ENTRY_REVISION, + sizeof(VARIABLE_POLICY_ENTRY) + sizeof(L"Wildcard#VarName##"), + sizeof(VARIABLE_POLICY_ENTRY), + TEST_GUID_1, + TEST_POLICY_MIN_SIZE_NULL, + TEST_POLICY_MAX_SIZE_NULL, + TEST_POLICY_ATTRIBUTES_NULL, + TEST_POLICY_ATTRIBUTES_NULL, + VARIABLE_POLICY_TYPE_NO_LOCK + }, + L"Wildcard#VarName##" + }; + CHAR16 *CheckVar1Name = L"Wildcard1VarName12"; + CHAR16 *CheckVar2Name = L"Wildcard2VarName34"; + CHAR16 *CheckVarBName = L"WildcardBVarName56"; + CHAR16 *CheckVarHName = L"Wildcard#VarName56"; + + // Make sure that two different sets of wildcard numbers match. + UT_ASSERT_TRUE( EvaluatePolicyMatch( &MatchCheckPolicy.Header, CheckVar1Name, &mTestGuid1, NULL ) ); + UT_ASSERT_TRUE( EvaluatePolicyMatch( &MatchCheckPolicy.Header, CheckVar2Name, &mTestGuid1, NULL ) ); + + // Make sure that the non-number charaters don't match. + UT_ASSERT_FALSE( EvaluatePolicyMatch( &MatchCheckPolicy.Header, CheckVarBName, &mTestGuid1, NULL ) ); + + // Make sure that '#' signs don't match. + UT_ASSERT_FALSE( EvaluatePolicyMatch( &MatchCheckPolicy.Header, CheckVarHName, &mTestGuid1, NULL ) ); + + return UNIT_TEST_PASSED; +} + +UNIT_TEST_STATUS +EFIAPI +WildcardPoliciesShouldMatchDigitsAdvanced ( + IN UNIT_TEST_CONTEXT Context + ) +{ + SIMPLE_VARIABLE_POLICY_ENTRY MatchCheckPolicy = { + { + VARIABLE_POLICY_ENTRY_REVISION, + sizeof(VARIABLE_POLICY_ENTRY) + sizeof(TEST_300_HASHES_STRING), + sizeof(VARIABLE_POLICY_ENTRY), + TEST_GUID_1, + TEST_POLICY_MIN_SIZE_NULL, + TEST_POLICY_MAX_SIZE_NULL, + TEST_POLICY_ATTRIBUTES_NULL, + TEST_POLICY_ATTRIBUTES_NULL, + VARIABLE_POLICY_TYPE_NO_LOCK + }, + TEST_300_HASHES_STRING + }; + CHAR16 *CheckShorterString = L"01234567890123456789012345678901234567890123456789"; + CHAR16 *CheckValidString = L"01234567890123456789012345678901234567890123456789"\ + "01234567890123456789012345678901234567890123456789"\ + "01234567890123456789012345678901234567890123456789"\ + "01234567890123456789012345678901234567890123456789"\ + "01234567890123456789012345678901234567890123456789"\ + "01234567890123456789012345678901234567890123456789"; + CHAR16 *CheckLongerString = L"01234567890123456789012345678901234567890123456789"\ + "01234567890123456789012345678901234567890123456789"\ + "01234567890123456789012345678901234567890123456789"\ + "01234567890123456789012345678901234567890123456789"\ + "01234567890123456789012345678901234567890123456789"\ + "01234567890123456789012345678901234567890123456789"\ + "01234567890123456789012345678901234567890123456789"; + UINT8 MatchPriority; + + // Make sure that the shorter and the longer do not match. + UT_ASSERT_FALSE( EvaluatePolicyMatch( &MatchCheckPolicy.Header, CheckShorterString, &mTestGuid1, NULL ) ); + UT_ASSERT_FALSE( EvaluatePolicyMatch( &MatchCheckPolicy.Header, CheckLongerString, &mTestGuid1, NULL ) ); + + // Make sure that the valid one matches and has the expected priority. + UT_ASSERT_TRUE( EvaluatePolicyMatch( &MatchCheckPolicy.Header, CheckValidString, &mTestGuid1, &MatchPriority ) ); + UT_ASSERT_EQUAL( MatchPriority, MAX_UINT8 ); + + return UNIT_TEST_PASSED; +} + +UNIT_TEST_STATUS +EFIAPI +WildcardPoliciesShouldMatchNamespaces ( + IN UNIT_TEST_CONTEXT Context + ) +{ + VARIABLE_POLICY_ENTRY MatchCheckPolicy = { + VARIABLE_POLICY_ENTRY_REVISION, + sizeof(VARIABLE_POLICY_ENTRY), + sizeof(VARIABLE_POLICY_ENTRY), + TEST_GUID_1, + TEST_POLICY_MIN_SIZE_NULL, + TEST_POLICY_MAX_SIZE_NULL, + TEST_POLICY_ATTRIBUTES_NULL, + TEST_POLICY_ATTRIBUTES_NULL, + VARIABLE_POLICY_TYPE_NO_LOCK + }; + CHAR16 *CheckVar1Name = L"Wildcard1VarName12"; + CHAR16 *CheckVar2Name = L"Wildcard2VarName34"; + CHAR16 *CheckVarBName = L"WildcardBVarName56"; + CHAR16 *CheckVarHName = L"Wildcard#VarName56"; + + // Make sure that all names in the same namespace match. + UT_ASSERT_TRUE( EvaluatePolicyMatch( &MatchCheckPolicy, CheckVar1Name, &mTestGuid1, NULL ) ); + UT_ASSERT_TRUE( EvaluatePolicyMatch( &MatchCheckPolicy, CheckVar2Name, &mTestGuid1, NULL ) ); + UT_ASSERT_TRUE( EvaluatePolicyMatch( &MatchCheckPolicy, CheckVarBName, &mTestGuid1, NULL ) ); + UT_ASSERT_TRUE( EvaluatePolicyMatch( &MatchCheckPolicy, CheckVarHName, &mTestGuid1, NULL ) ); + + // Make sure that different namespace doesn't match. + UT_ASSERT_FALSE( EvaluatePolicyMatch( &MatchCheckPolicy, CheckVar1Name, &mTestGuid2, NULL ) ); + + + return UNIT_TEST_PASSED; +} + +UNIT_TEST_STATUS +EFIAPI +MatchPrioritiesShouldFollowRules ( + IN UNIT_TEST_CONTEXT Context + ) +{ + SIMPLE_VARIABLE_POLICY_ENTRY MatchCheckPolicy = { + { + VARIABLE_POLICY_ENTRY_REVISION, + sizeof(VARIABLE_POLICY_ENTRY) + sizeof(L"Wildcard1VarName12"), + sizeof(VARIABLE_POLICY_ENTRY), + TEST_GUID_1, + TEST_POLICY_MIN_SIZE_NULL, + TEST_POLICY_MAX_SIZE_NULL, + TEST_POLICY_ATTRIBUTES_NULL, + TEST_POLICY_ATTRIBUTES_NULL, + VARIABLE_POLICY_TYPE_NO_LOCK + }, + L"Wildcard1VarName12" + }; + CHAR16 CheckVar1Name[] = L"Wildcard1VarName12"; + CHAR16 MatchVar1Name[] = L"Wildcard1VarName12"; + CHAR16 MatchVar2Name[] = L"Wildcard#VarName12"; + CHAR16 MatchVar3Name[] = L"Wildcard#VarName#2"; + CHAR16 MatchVar4Name[] = L"Wildcard#VarName##"; + UINT8 MatchPriority; + + // Check with a perfect match. + CopyMem( &MatchCheckPolicy.Name, MatchVar1Name, sizeof(MatchVar1Name) ); + UT_ASSERT_TRUE( EvaluatePolicyMatch( &MatchCheckPolicy.Header, CheckVar1Name, &mTestGuid1, &MatchPriority ) ); + UT_ASSERT_EQUAL( MatchPriority, 0 ); + + // Check with progressively lower priority matches. + CopyMem( &MatchCheckPolicy.Name, MatchVar2Name, sizeof(MatchVar2Name) ); + UT_ASSERT_TRUE( EvaluatePolicyMatch( &MatchCheckPolicy.Header, CheckVar1Name, &mTestGuid1, &MatchPriority ) ); + UT_ASSERT_EQUAL( MatchPriority, 1 ); + CopyMem( &MatchCheckPolicy.Name, MatchVar3Name, sizeof(MatchVar3Name) ); + UT_ASSERT_TRUE( EvaluatePolicyMatch( &MatchCheckPolicy.Header, CheckVar1Name, &mTestGuid1, &MatchPriority ) ); + UT_ASSERT_EQUAL( MatchPriority, 2 ); + CopyMem( &MatchCheckPolicy.Name, MatchVar4Name, sizeof(MatchVar4Name) ); + UT_ASSERT_TRUE( EvaluatePolicyMatch( &MatchCheckPolicy.Header, CheckVar1Name, &mTestGuid1, &MatchPriority ) ); + UT_ASSERT_EQUAL( MatchPriority, 3 ); + + // Check against the entire namespace. + MatchCheckPolicy.Header.Size = sizeof(VARIABLE_POLICY_ENTRY); + UT_ASSERT_TRUE( EvaluatePolicyMatch( &MatchCheckPolicy.Header, CheckVar1Name, &mTestGuid1, &MatchPriority ) ); + UT_ASSERT_EQUAL( MatchPriority, MAX_UINT8 ); + + return UNIT_TEST_PASSED; +} + +#endif // INTERNAL_UNIT_TEST + + +///=== POLICY MANIPULATION SUITE ============================================== + +UNIT_TEST_STATUS +EFIAPI +RegisterShouldAllowNamespaceWildcards ( + IN UNIT_TEST_CONTEXT Context + ) +{ + SIMPLE_VARIABLE_POLICY_ENTRY ValidationPolicy = { + { + VARIABLE_POLICY_ENTRY_REVISION, + sizeof(VARIABLE_POLICY_ENTRY), + sizeof(VARIABLE_POLICY_ENTRY), + TEST_GUID_1, + TEST_POLICY_MIN_SIZE_NULL, + TEST_POLICY_MAX_SIZE_NULL, + TEST_POLICY_ATTRIBUTES_NULL, + TEST_POLICY_ATTRIBUTES_NULL, + VARIABLE_POLICY_TYPE_NO_LOCK + }, + L"" + }; + + UT_ASSERT_NOT_EFI_ERROR( RegisterVariablePolicy( &ValidationPolicy.Header ) ); + + return UNIT_TEST_PASSED; +} + +UNIT_TEST_STATUS +EFIAPI +RegisterShouldAllowStateVarsForNamespaces ( + IN UNIT_TEST_CONTEXT Context + ) +{ + EXPANDED_VARIABLE_POLICY_ENTRY ValidationPolicy = { + { + VARIABLE_POLICY_ENTRY_REVISION, + 0, // Will be populated by init helper. + 0, // Will be populated by init helper. + TEST_GUID_1, + TEST_POLICY_MIN_SIZE_NULL, + TEST_POLICY_MAX_SIZE_NULL, + TEST_POLICY_ATTRIBUTES_NULL, + TEST_POLICY_ATTRIBUTES_NULL, + VARIABLE_POLICY_TYPE_LOCK_ON_VAR_STATE + }, + { + TEST_GUID_2, + 1, // Value + 0 // Padding + }, + L"", + L"" + }; + UT_ASSERT_TRUE( InitExpVarPolicyStrings( &ValidationPolicy, NULL, TEST_VAR_2_NAME ) ); + + UT_ASSERT_NOT_EFI_ERROR( RegisterVariablePolicy( &ValidationPolicy.Header ) ); + + return UNIT_TEST_PASSED; +} + +UNIT_TEST_STATUS +EFIAPI +RegisterShouldRejectNullPointers ( + IN UNIT_TEST_CONTEXT Context + ) +{ + UT_ASSERT_EQUAL( RegisterVariablePolicy( NULL ), EFI_INVALID_PARAMETER ); + return UNIT_TEST_PASSED; +} + +UNIT_TEST_STATUS +EFIAPI +RegisterShouldRejectBadRevisions ( + IN UNIT_TEST_CONTEXT Context + ) +{ + SIMPLE_VARIABLE_POLICY_ENTRY ValidationPolicy = { + { + VARIABLE_POLICY_ENTRY_REVISION, + sizeof(VARIABLE_POLICY_ENTRY) + sizeof(TEST_VAR_1_NAME), + sizeof(VARIABLE_POLICY_ENTRY), + TEST_GUID_1, + TEST_POLICY_MIN_SIZE_NULL, + TEST_POLICY_MAX_SIZE_NULL, + TEST_POLICY_ATTRIBUTES_NULL, + TEST_POLICY_ATTRIBUTES_NULL, + VARIABLE_POLICY_TYPE_NO_LOCK + }, + TEST_VAR_1_NAME + }; + + ValidationPolicy.Header.Version = MAX_UINT32; + UT_ASSERT_EQUAL( RegisterVariablePolicy( &ValidationPolicy.Header ), EFI_INVALID_PARAMETER ); + + return UNIT_TEST_PASSED; +} + +UNIT_TEST_STATUS +EFIAPI +RegisterShouldRejectBadSizes ( + IN UNIT_TEST_CONTEXT Context + ) +{ + SIMPLE_VARIABLE_POLICY_ENTRY ValidationPolicy = { + { + VARIABLE_POLICY_ENTRY_REVISION, + sizeof(VARIABLE_POLICY_ENTRY) + sizeof(TEST_VAR_1_NAME), + sizeof(VARIABLE_POLICY_ENTRY), + TEST_GUID_1, + TEST_POLICY_MIN_SIZE_NULL, + TEST_POLICY_MAX_SIZE_NULL, + TEST_POLICY_ATTRIBUTES_NULL, + TEST_POLICY_ATTRIBUTES_NULL, + VARIABLE_POLICY_TYPE_NO_LOCK + }, + TEST_VAR_1_NAME + }; + + ValidationPolicy.Header.Size = sizeof(VARIABLE_POLICY_ENTRY) - 2; + UT_ASSERT_EQUAL( RegisterVariablePolicy( &ValidationPolicy.Header ), EFI_INVALID_PARAMETER ); + + return UNIT_TEST_PASSED; +} + +UNIT_TEST_STATUS +EFIAPI +RegisterShouldRejectBadOffsets ( + IN UNIT_TEST_CONTEXT Context + ) +{ + EXPANDED_VARIABLE_POLICY_ENTRY ValidationPolicy = { + { + VARIABLE_POLICY_ENTRY_REVISION, + 0, // Will be populated by init helper. + 0, // Will be populated by init helper. + TEST_GUID_1, + TEST_POLICY_MIN_SIZE_NULL, + TEST_POLICY_MAX_SIZE_NULL, + TEST_POLICY_ATTRIBUTES_NULL, + TEST_POLICY_ATTRIBUTES_NULL, + VARIABLE_POLICY_TYPE_LOCK_ON_VAR_STATE + }, + { + TEST_GUID_2, + 1, // Value + 0 // Padding + }, + L"", + L"" + }; + UT_ASSERT_TRUE( InitExpVarPolicyStrings( &ValidationPolicy, TEST_VAR_1_NAME, TEST_VAR_2_NAME ) ); + + // Check for an offset outside the size bounds. + ValidationPolicy.Header.OffsetToName = ValidationPolicy.Header.Size + 1; + UT_ASSERT_EQUAL( RegisterVariablePolicy( &ValidationPolicy.Header ), EFI_INVALID_PARAMETER ); + + // Check for an offset inside the policy header. + ValidationPolicy.Header.OffsetToName = sizeof(VARIABLE_POLICY_ENTRY) - 2; + UT_ASSERT_EQUAL( RegisterVariablePolicy( &ValidationPolicy.Header ), EFI_INVALID_PARAMETER ); + + // Check for an offset inside the state policy header. + ValidationPolicy.Header.OffsetToName = sizeof(VARIABLE_POLICY_ENTRY) + 2; + UT_ASSERT_EQUAL( RegisterVariablePolicy( &ValidationPolicy.Header ), EFI_INVALID_PARAMETER ); + + // Check for a ridiculous offset. + ValidationPolicy.Header.OffsetToName = MAX_UINT16; + UT_ASSERT_EQUAL( RegisterVariablePolicy( &ValidationPolicy.Header ), EFI_INVALID_PARAMETER ); + + return UNIT_TEST_PASSED; +} + +UNIT_TEST_STATUS +EFIAPI +RegisterShouldRejectMissingStateStrings ( + IN UNIT_TEST_CONTEXT Context + ) +{ + EXPANDED_VARIABLE_POLICY_ENTRY ValidationPolicy = { + { + VARIABLE_POLICY_ENTRY_REVISION, + 0, // Will be populated by init helper. + 0, // Will be populated by init helper. + TEST_GUID_1, + TEST_POLICY_MIN_SIZE_NULL, + TEST_POLICY_MAX_SIZE_NULL, + TEST_POLICY_ATTRIBUTES_NULL, + TEST_POLICY_ATTRIBUTES_NULL, + VARIABLE_POLICY_TYPE_LOCK_ON_VAR_STATE + }, + { + TEST_GUID_2, + 1, // Value + 0 // Padding + }, + L"", + L"" + }; + UT_ASSERT_TRUE( InitExpVarPolicyStrings( &ValidationPolicy, TEST_VAR_1_NAME, TEST_VAR_2_NAME ) ); + + // Remove the state string and copy the Name into it's place. + // Also adjust the offset. + ValidationPolicy.Header.Size = sizeof(VARIABLE_POLICY_ENTRY) + sizeof(VARIABLE_LOCK_ON_VAR_STATE_POLICY) + sizeof(TEST_VAR_1_NAME); + ValidationPolicy.Header.OffsetToName = sizeof(VARIABLE_POLICY_ENTRY) + sizeof(VARIABLE_LOCK_ON_VAR_STATE_POLICY); + CopyMem( (UINT8*)&ValidationPolicy + ValidationPolicy.Header.OffsetToName, TEST_VAR_1_NAME, sizeof(TEST_VAR_1_NAME) ); + + // Make sure that this structure fails. + UT_ASSERT_EQUAL( RegisterVariablePolicy( &ValidationPolicy.Header ), EFI_INVALID_PARAMETER ); + + return UNIT_TEST_PASSED; +} + +UNIT_TEST_STATUS +EFIAPI +RegisterShouldRejectStringsMissingNull ( + IN UNIT_TEST_CONTEXT Context + ) +{ + EXPANDED_VARIABLE_POLICY_ENTRY ValidationPolicy = { + { + VARIABLE_POLICY_ENTRY_REVISION, + 0, // Will be populated by init helper. + 0, // Will be populated by init helper. + TEST_GUID_1, + TEST_POLICY_MIN_SIZE_NULL, + TEST_POLICY_MAX_SIZE_NULL, + TEST_POLICY_ATTRIBUTES_NULL, + TEST_POLICY_ATTRIBUTES_NULL, + VARIABLE_POLICY_TYPE_LOCK_ON_VAR_STATE + }, + { + TEST_GUID_2, + 1, // Value + 0 // Padding + }, + L"", + L"" + }; + UT_ASSERT_TRUE( InitExpVarPolicyStrings( &ValidationPolicy, TEST_VAR_1_NAME, TEST_VAR_2_NAME ) ); + + // Removing the NULL from the Name should fail. + ValidationPolicy.Header.Size = ValidationPolicy.Header.Size - sizeof(CHAR16); + UT_ASSERT_EQUAL( RegisterVariablePolicy( &ValidationPolicy.Header ), EFI_INVALID_PARAMETER ); + + // Removing the NULL from the State Name is a little trickier. + // Copy the Name up one byte. + ValidationPolicy.Header.OffsetToName = ValidationPolicy.Header.OffsetToName - sizeof(CHAR16); + CopyMem( (UINT8*)&ValidationPolicy + ValidationPolicy.Header.OffsetToName, TEST_VAR_1_NAME, sizeof(TEST_VAR_1_NAME) ); + UT_ASSERT_EQUAL( RegisterVariablePolicy( &ValidationPolicy.Header ), EFI_INVALID_PARAMETER ); + + return UNIT_TEST_PASSED; +} + +UNIT_TEST_STATUS +EFIAPI +RegisterShouldRejectMalformedStrings ( + IN UNIT_TEST_CONTEXT Context + ) +{ + EXPANDED_VARIABLE_POLICY_ENTRY ValidationPolicy = { + { + VARIABLE_POLICY_ENTRY_REVISION, + 0, // Will be populated by init helper. + 0, // Will be populated by init helper. + TEST_GUID_1, + TEST_POLICY_MIN_SIZE_NULL, + TEST_POLICY_MAX_SIZE_NULL, + TEST_POLICY_ATTRIBUTES_NULL, + TEST_POLICY_ATTRIBUTES_NULL, + VARIABLE_POLICY_TYPE_LOCK_ON_VAR_STATE + }, + { + TEST_GUID_2, + 1, // Value + 0 // Padding + }, + L"", + L"" + }; + UT_ASSERT_TRUE( InitExpVarPolicyStrings( &ValidationPolicy, TEST_VAR_1_NAME, TEST_VAR_2_NAME ) ); + + // Bisecting the NULL from the Name should fail. + ValidationPolicy.Header.Size = ValidationPolicy.Header.Size - 1; + UT_ASSERT_EQUAL( RegisterVariablePolicy( &ValidationPolicy.Header ), EFI_INVALID_PARAMETER ); + + // Bisecting the NULL from the State Name is a little trickier. + // Copy the Name up one byte. + ValidationPolicy.Header.OffsetToName = ValidationPolicy.Header.OffsetToName - 1; + CopyMem( (UINT8*)&ValidationPolicy + ValidationPolicy.Header.OffsetToName, TEST_VAR_1_NAME, sizeof(TEST_VAR_1_NAME) ); + UT_ASSERT_EQUAL( RegisterVariablePolicy( &ValidationPolicy.Header ), EFI_INVALID_PARAMETER ); + + return UNIT_TEST_PASSED; +} + +UNIT_TEST_STATUS +EFIAPI +RegisterShouldRejectUnpackedPolicies ( + IN UNIT_TEST_CONTEXT Context + ) +{ + EXPANDED_VARIABLE_POLICY_ENTRY ValidationPolicy = { + { + VARIABLE_POLICY_ENTRY_REVISION, + 0, // Will be populated by init helper. + 0, // Will be populated by init helper. + TEST_GUID_1, + TEST_POLICY_MIN_SIZE_NULL, + TEST_POLICY_MAX_SIZE_NULL, + TEST_POLICY_ATTRIBUTES_NULL, + TEST_POLICY_ATTRIBUTES_NULL, + VARIABLE_POLICY_TYPE_LOCK_ON_VAR_STATE + }, + { + TEST_GUID_2, + 1, // Value + 0 // Padding + }, + L"", + L"" + }; + UT_ASSERT_TRUE( InitExpVarPolicyStrings( &ValidationPolicy, TEST_VAR_1_NAME, TEST_VAR_2_NAME ) ); + + // Increase the size and move the Name out a bit. + ValidationPolicy.Header.Size = ValidationPolicy.Header.Size + sizeof(CHAR16); + ValidationPolicy.Header.OffsetToName = ValidationPolicy.Header.OffsetToName + sizeof(CHAR16); + CopyMem( (UINT8*)&ValidationPolicy + ValidationPolicy.Header.OffsetToName, TEST_VAR_1_NAME, sizeof(TEST_VAR_1_NAME) ); + UT_ASSERT_EQUAL( RegisterVariablePolicy( &ValidationPolicy.Header ), EFI_INVALID_PARAMETER ); + + // Reintialize without the state policy and try the same test. + ValidationPolicy.Header.LockPolicyType = VARIABLE_POLICY_TYPE_NO_LOCK; + UT_ASSERT_TRUE( InitExpVarPolicyStrings( &ValidationPolicy, TEST_VAR_1_NAME, NULL ) ); + ValidationPolicy.Header.Size = ValidationPolicy.Header.Size + sizeof(CHAR16); + ValidationPolicy.Header.OffsetToName = ValidationPolicy.Header.OffsetToName + sizeof(CHAR16); + CopyMem( (UINT8*)&ValidationPolicy + ValidationPolicy.Header.OffsetToName, TEST_VAR_1_NAME, sizeof(TEST_VAR_1_NAME) ); + UT_ASSERT_EQUAL( RegisterVariablePolicy( &ValidationPolicy.Header ), EFI_INVALID_PARAMETER ); + + return UNIT_TEST_PASSED; +} + +UNIT_TEST_STATUS +EFIAPI +RegisterShouldRejectInvalidNameCharacters ( + IN UNIT_TEST_CONTEXT Context + ) +{ + // EXPANDED_VARIABLE_POLICY_ENTRY ValidationPolicy = { + // { + // VARIABLE_POLICY_ENTRY_REVISION, + // 0, // Will be populated by init helper. + // 0, // Will be populated by init helper. + // TEST_GUID_1, + // TEST_POLICY_MIN_SIZE_NULL, + // TEST_POLICY_MAX_SIZE_NULL, + // TEST_POLICY_ATTRIBUTES_NULL, + // TEST_POLICY_ATTRIBUTES_NULL, + // VARIABLE_POLICY_TYPE_LOCK_ON_VAR_STATE + // }, + // { + // TEST_GUID_2, + // 1, // Value + // 0 // Padding + // }, + // L"", + // L"" + // }; + + // Currently, there are no known invalid characters. + // '#' in LockPolicy->Name are taken as literal. + + return UNIT_TEST_PASSED; +} + +UNIT_TEST_STATUS +EFIAPI +RegisterShouldRejectBadPolicyConstraints ( + IN UNIT_TEST_CONTEXT Context + ) +{ + SIMPLE_VARIABLE_POLICY_ENTRY ValidationPolicy = { + { + VARIABLE_POLICY_ENTRY_REVISION, + sizeof(VARIABLE_POLICY_ENTRY) + sizeof(TEST_VAR_1_NAME), + sizeof(VARIABLE_POLICY_ENTRY), + TEST_GUID_1, + TEST_POLICY_MIN_SIZE_NULL, + TEST_POLICY_MAX_SIZE_NULL, + TEST_POLICY_ATTRIBUTES_NULL, + TEST_POLICY_ATTRIBUTES_NULL, + VARIABLE_POLICY_TYPE_NO_LOCK + }, + TEST_VAR_1_NAME + }; + + // Make sure that invalid MAXes are rejected. + ValidationPolicy.Header.MaxSize = 0; + UT_ASSERT_EQUAL( RegisterVariablePolicy( &ValidationPolicy.Header ), EFI_INVALID_PARAMETER ); + + return UNIT_TEST_PASSED; +} + +UNIT_TEST_STATUS +EFIAPI +RegisterShouldRejectUnknownLockPolicies ( + IN UNIT_TEST_CONTEXT Context + ) +{ + SIMPLE_VARIABLE_POLICY_ENTRY ValidationPolicy = { + { + VARIABLE_POLICY_ENTRY_REVISION, + sizeof(VARIABLE_POLICY_ENTRY) + sizeof(TEST_VAR_1_NAME), + sizeof(VARIABLE_POLICY_ENTRY), + TEST_GUID_1, + TEST_POLICY_MIN_SIZE_NULL, + TEST_POLICY_MAX_SIZE_NULL, + TEST_POLICY_ATTRIBUTES_NULL, + TEST_POLICY_ATTRIBUTES_NULL, + VARIABLE_POLICY_TYPE_NO_LOCK + }, + TEST_VAR_1_NAME + }; + + ValidationPolicy.Header.LockPolicyType = VARIABLE_POLICY_TYPE_LOCK_ON_VAR_STATE + 1; + UT_ASSERT_EQUAL( RegisterVariablePolicy( &ValidationPolicy.Header ), EFI_INVALID_PARAMETER ); + ValidationPolicy.Header.LockPolicyType = VARIABLE_POLICY_TYPE_LOCK_ON_VAR_STATE + 1; + UT_ASSERT_EQUAL( RegisterVariablePolicy( &ValidationPolicy.Header ), EFI_INVALID_PARAMETER ); + + return UNIT_TEST_PASSED; +} + +UNIT_TEST_STATUS +EFIAPI +RegisterShouldRejectPolicesWithTooManyWildcards ( + IN UNIT_TEST_CONTEXT Context + ) +{ + SIMPLE_VARIABLE_POLICY_ENTRY ValidationPolicy = { + { + VARIABLE_POLICY_ENTRY_REVISION, + sizeof(VARIABLE_POLICY_ENTRY) + sizeof(TEST_300_HASHES_STRING), + sizeof(VARIABLE_POLICY_ENTRY), + TEST_GUID_1, + TEST_POLICY_MIN_SIZE_NULL, + TEST_POLICY_MAX_SIZE_NULL, + TEST_POLICY_ATTRIBUTES_NULL, + TEST_POLICY_ATTRIBUTES_NULL, + VARIABLE_POLICY_TYPE_NO_LOCK + }, + TEST_300_HASHES_STRING + }; + + // 300 Hashes is currently larger than the possible maximum match priority. + UT_ASSERT_EQUAL( RegisterVariablePolicy( &ValidationPolicy.Header ), EFI_INVALID_PARAMETER ); + + return UNIT_TEST_PASSED; +} + +UNIT_TEST_STATUS +EFIAPI +RegisterShouldRejectDuplicatePolicies ( + IN UNIT_TEST_CONTEXT Context + ) +{ + SIMPLE_VARIABLE_POLICY_ENTRY ValidationPolicy = { + { + VARIABLE_POLICY_ENTRY_REVISION, + sizeof(VARIABLE_POLICY_ENTRY) + sizeof(TEST_VAR_1_NAME), + sizeof(VARIABLE_POLICY_ENTRY), + TEST_GUID_1, + TEST_POLICY_MIN_SIZE_NULL, + TEST_POLICY_MAX_SIZE_NULL, + TEST_POLICY_ATTRIBUTES_NULL, + TEST_POLICY_ATTRIBUTES_NULL, + VARIABLE_POLICY_TYPE_NO_LOCK + }, + TEST_VAR_1_NAME + }; + + UT_ASSERT_NOT_EFI_ERROR( RegisterVariablePolicy( &ValidationPolicy.Header ) ); + UT_ASSERT_STATUS_EQUAL( RegisterVariablePolicy( &ValidationPolicy.Header ), EFI_ALREADY_STARTED ); + + return UNIT_TEST_PASSED; +} + +UNIT_TEST_STATUS +EFIAPI +MinAndMaxSizePoliciesShouldBeHonored ( + IN UNIT_TEST_CONTEXT Context + ) +{ + SIMPLE_VARIABLE_POLICY_ENTRY ValidationPolicy = { + { + VARIABLE_POLICY_ENTRY_REVISION, + sizeof(VARIABLE_POLICY_ENTRY) + sizeof(TEST_VAR_1_NAME), + sizeof(VARIABLE_POLICY_ENTRY), + TEST_GUID_1, + TEST_POLICY_MIN_SIZE_10, + TEST_POLICY_MAX_SIZE_200, + TEST_POLICY_ATTRIBUTES_NULL, + TEST_POLICY_ATTRIBUTES_NULL, + VARIABLE_POLICY_TYPE_NO_LOCK + }, + TEST_VAR_1_NAME + }; + EFI_STATUS PolicyCheck; + UINT8 DummyData[TEST_POLICY_MAX_SIZE_200+1]; + + + // Without a policy, there should be no constraints on variable creation. + PolicyCheck = ValidateSetVariable( TEST_VAR_1_NAME, + &mTestGuid1, + VARIABLE_ATTRIBUTE_NV_BS, + TEST_POLICY_MAX_SIZE_200+1, + DummyData ); + UT_ASSERT_NOT_EFI_ERROR( PolicyCheck ); + + // Set a policy to test against. + UT_ASSERT_NOT_EFI_ERROR( RegisterVariablePolicy( &ValidationPolicy.Header ) ); + + // With a policy, make sure that sizes outsize the target range fail. + PolicyCheck = ValidateSetVariable( TEST_VAR_1_NAME, + &mTestGuid1, + VARIABLE_ATTRIBUTE_NV_BS, + TEST_POLICY_MAX_SIZE_200+1, + DummyData ); + UT_ASSERT_TRUE( EFI_ERROR( PolicyCheck ) ); + + // With a policy, make sure that sizes outsize the target range fail. + PolicyCheck = ValidateSetVariable( TEST_VAR_1_NAME, + &mTestGuid1, + VARIABLE_ATTRIBUTE_NV_BS, + TEST_POLICY_MIN_SIZE_10-1, + DummyData ); + UT_ASSERT_TRUE( EFI_ERROR( PolicyCheck ) ); + + // With a policy, make sure a valid variable is still valid. + PolicyCheck = ValidateSetVariable( TEST_VAR_1_NAME, + &mTestGuid1, + VARIABLE_ATTRIBUTE_NV_BS, + TEST_POLICY_MIN_SIZE_10+1, + DummyData ); + UT_ASSERT_NOT_EFI_ERROR( PolicyCheck ); + + return UNIT_TEST_PASSED; +} + +UNIT_TEST_STATUS +EFIAPI +AttributeMustPoliciesShouldBeHonored ( + IN UNIT_TEST_CONTEXT Context + ) +{ + SIMPLE_VARIABLE_POLICY_ENTRY ValidationPolicy = { + { + VARIABLE_POLICY_ENTRY_REVISION, + sizeof(VARIABLE_POLICY_ENTRY) + sizeof(TEST_VAR_1_NAME), + sizeof(VARIABLE_POLICY_ENTRY), + TEST_GUID_1, + TEST_POLICY_MIN_SIZE_NULL, + TEST_POLICY_MAX_SIZE_NULL, + VARIABLE_ATTRIBUTE_NV_BS_RT, + TEST_POLICY_ATTRIBUTES_NULL, + VARIABLE_POLICY_TYPE_NO_LOCK + }, + TEST_VAR_1_NAME + }; + EFI_STATUS PolicyCheck; + UINT8 DummyData[12]; + + + // Without a policy, there should be no constraints on variable creation. + PolicyCheck = ValidateSetVariable( TEST_VAR_1_NAME, + &mTestGuid1, + TEST_POLICY_ATTRIBUTES_NULL, + sizeof(DummyData), + DummyData ); + UT_ASSERT_NOT_EFI_ERROR( PolicyCheck ); + + // Set a policy to test against. + UT_ASSERT_NOT_EFI_ERROR( RegisterVariablePolicy( &ValidationPolicy.Header ) ); + + // With a policy, make sure that no attributes fail. + PolicyCheck = ValidateSetVariable( TEST_VAR_1_NAME, + &mTestGuid1, + TEST_POLICY_ATTRIBUTES_NULL, + sizeof(DummyData), + DummyData ); + UT_ASSERT_TRUE( EFI_ERROR( PolicyCheck ) ); + + // With a policy, make sure that some -- but not all -- attributes fail. + PolicyCheck = ValidateSetVariable( TEST_VAR_1_NAME, + &mTestGuid1, + VARIABLE_ATTRIBUTE_BS_RT_AT, + sizeof(DummyData), + DummyData ); + UT_ASSERT_TRUE( EFI_ERROR( PolicyCheck ) ); + + // With a policy, make sure that all attributes pass. + PolicyCheck = ValidateSetVariable( TEST_VAR_1_NAME, + &mTestGuid1, + VARIABLE_ATTRIBUTE_NV_BS_RT, + sizeof(DummyData), + DummyData ); + UT_ASSERT_NOT_EFI_ERROR( PolicyCheck ); + + // With a policy, make sure that all attributes -- plus some -- pass. + PolicyCheck = ValidateSetVariable( TEST_VAR_1_NAME, + &mTestGuid1, + VARIABLE_ATTRIBUTE_NV_BS_RT_AT, + sizeof(DummyData), + DummyData ); + UT_ASSERT_NOT_EFI_ERROR( PolicyCheck ); + + return UNIT_TEST_PASSED; +} + +UNIT_TEST_STATUS +EFIAPI +AttributeCantPoliciesShouldBeHonored ( + IN UNIT_TEST_CONTEXT Context + ) +{ + SIMPLE_VARIABLE_POLICY_ENTRY ValidationPolicy = { + { + VARIABLE_POLICY_ENTRY_REVISION, + sizeof(VARIABLE_POLICY_ENTRY) + sizeof(TEST_VAR_1_NAME), + sizeof(VARIABLE_POLICY_ENTRY), + TEST_GUID_1, + TEST_POLICY_MIN_SIZE_NULL, + TEST_POLICY_MAX_SIZE_NULL, + TEST_POLICY_ATTRIBUTES_NULL, + EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS, + VARIABLE_POLICY_TYPE_NO_LOCK + }, + TEST_VAR_1_NAME + }; + EFI_STATUS PolicyCheck; + UINT8 DummyData[12]; + + + // Without a policy, there should be no constraints on variable creation. + PolicyCheck = ValidateSetVariable( TEST_VAR_1_NAME, + &mTestGuid1, + VARIABLE_ATTRIBUTE_BS_RT_AT, + sizeof(DummyData), + DummyData ); + UT_ASSERT_NOT_EFI_ERROR( PolicyCheck ); + + // Set a policy to test against. + UT_ASSERT_NOT_EFI_ERROR( RegisterVariablePolicy( &ValidationPolicy.Header ) ); + + // With a policy, make sure that forbidden attributes fail. + PolicyCheck = ValidateSetVariable( TEST_VAR_1_NAME, + &mTestGuid1, + EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS, + sizeof(DummyData), + DummyData ); + UT_ASSERT_TRUE( EFI_ERROR( PolicyCheck ) ); + + // With a policy, make sure that a mixture of attributes -- including the forbidden -- fail. + PolicyCheck = ValidateSetVariable( TEST_VAR_1_NAME, + &mTestGuid1, + VARIABLE_ATTRIBUTE_BS_RT_AT, + sizeof(DummyData), + DummyData ); + UT_ASSERT_TRUE( EFI_ERROR( PolicyCheck ) ); + + // With a policy, make sure that attributes without the forbidden pass. + PolicyCheck = ValidateSetVariable( TEST_VAR_1_NAME, + &mTestGuid1, + VARIABLE_ATTRIBUTE_NV_BS_RT, + sizeof(DummyData), + DummyData ); + UT_ASSERT_NOT_EFI_ERROR( PolicyCheck ); + + return UNIT_TEST_PASSED; +} + +UNIT_TEST_STATUS +EFIAPI +VariablesShouldBeDeletableRegardlessOfSize ( + IN UNIT_TEST_CONTEXT Context + ) +{ + SIMPLE_VARIABLE_POLICY_ENTRY ValidationPolicy = { + { + VARIABLE_POLICY_ENTRY_REVISION, + sizeof(VARIABLE_POLICY_ENTRY) + sizeof(TEST_VAR_1_NAME), + sizeof(VARIABLE_POLICY_ENTRY), + TEST_GUID_1, + TEST_POLICY_MIN_SIZE_10, + TEST_POLICY_MAX_SIZE_NULL, + TEST_POLICY_ATTRIBUTES_NULL, + EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS, + VARIABLE_POLICY_TYPE_NO_LOCK + }, + TEST_VAR_1_NAME + }; + EFI_STATUS PolicyCheck; + UINT8 DummyData[TEST_POLICY_MAX_SIZE_200+1]; + + // Create a policy enforcing a minimum variable size. + UT_ASSERT_NOT_EFI_ERROR( RegisterVariablePolicy( &ValidationPolicy.Header ) ); + + // Make sure that a normal set would fail. + PolicyCheck = ValidateSetVariable( TEST_VAR_1_NAME, + &mTestGuid1, + VARIABLE_ATTRIBUTE_NV_BS, + TEST_POLICY_MIN_SIZE_10-1, + DummyData ); + UT_ASSERT_STATUS_EQUAL( PolicyCheck, EFI_INVALID_PARAMETER ); + + // Now make sure that a delete would succeed. + PolicyCheck = ValidateSetVariable( TEST_VAR_1_NAME, + &mTestGuid1, + VARIABLE_ATTRIBUTE_NV_BS, + 0, + NULL ); + UT_ASSERT_NOT_EFI_ERROR( PolicyCheck ); + + return UNIT_TEST_PASSED; +} + +UNIT_TEST_STATUS +EFIAPI +LockNowPoliciesShouldBeHonored ( + IN UNIT_TEST_CONTEXT Context + ) +{ + SIMPLE_VARIABLE_POLICY_ENTRY ValidationPolicy = { + { + VARIABLE_POLICY_ENTRY_REVISION, + sizeof(VARIABLE_POLICY_ENTRY) + sizeof(TEST_VAR_1_NAME), + sizeof(VARIABLE_POLICY_ENTRY), + TEST_GUID_1, + TEST_POLICY_MIN_SIZE_NULL, + TEST_POLICY_MAX_SIZE_NULL, + TEST_POLICY_ATTRIBUTES_NULL, + TEST_POLICY_ATTRIBUTES_NULL, + VARIABLE_POLICY_TYPE_LOCK_NOW + }, + TEST_VAR_1_NAME + }; + EFI_STATUS PolicyCheck; + UINT8 DummyData[12]; + + + // Without a policy, there should be no constraints on variable creation. + PolicyCheck = ValidateSetVariable( TEST_VAR_1_NAME, + &mTestGuid1, + VARIABLE_ATTRIBUTE_BS_RT_AT, + sizeof(DummyData), + DummyData ); + UT_ASSERT_NOT_EFI_ERROR( PolicyCheck ); + + // Set a policy to test against. + UT_ASSERT_NOT_EFI_ERROR( RegisterVariablePolicy( &ValidationPolicy.Header ) ); + + // With a policy, make sure that writes immediately fail. + PolicyCheck = ValidateSetVariable( TEST_VAR_1_NAME, + &mTestGuid1, + VARIABLE_ATTRIBUTE_BS_RT_AT, + sizeof(DummyData), + DummyData ); + UT_ASSERT_TRUE( EFI_ERROR( PolicyCheck ) ); + + return UNIT_TEST_PASSED; +} + +UNIT_TEST_STATUS +EFIAPI +LockOnCreatePoliciesShouldBeHonored ( + IN UNIT_TEST_CONTEXT Context + ) +{ + SIMPLE_VARIABLE_POLICY_ENTRY ValidationPolicy = { + { + VARIABLE_POLICY_ENTRY_REVISION, + sizeof(VARIABLE_POLICY_ENTRY) + sizeof(TEST_VAR_1_NAME), + sizeof(VARIABLE_POLICY_ENTRY), + TEST_GUID_1, + TEST_POLICY_MIN_SIZE_NULL, + TEST_POLICY_MAX_SIZE_NULL, + TEST_POLICY_ATTRIBUTES_NULL, + TEST_POLICY_ATTRIBUTES_NULL, + VARIABLE_POLICY_TYPE_LOCK_ON_CREATE + }, + TEST_VAR_1_NAME + }; + EFI_STATUS PolicyCheck; + UINT8 DummyData[12]; + UINTN ExpectedDataSize; + + + // Without a policy, there should be no constraints on variable creation. + PolicyCheck = ValidateSetVariable( TEST_VAR_1_NAME, + &mTestGuid1, + VARIABLE_ATTRIBUTE_BS_RT_AT, + sizeof(DummyData), + DummyData ); + UT_ASSERT_NOT_EFI_ERROR( PolicyCheck ); + + // Set a policy to test against. + UT_ASSERT_NOT_EFI_ERROR( RegisterVariablePolicy( &ValidationPolicy.Header ) ); + + // Set consistent expectations on what the calls are looking for. + expect_memory_count( StubGetVariableNull, VariableName, TEST_VAR_1_NAME, sizeof(TEST_VAR_1_NAME), 2 ); + expect_memory_count( StubGetVariableNull, VendorGuid, &mTestGuid1, sizeof(mTestGuid1), 2 ); + ExpectedDataSize = 0; + expect_memory_count( StubGetVariableNull, DataSize, &ExpectedDataSize, sizeof(ExpectedDataSize), 2 ); + + // With a policy, make sure that writes still work, since the variable doesn't exist. + will_return( StubGetVariableNull, TEST_POLICY_ATTRIBUTES_NULL ); // Attributes + will_return( StubGetVariableNull, 0 ); // Size + will_return( StubGetVariableNull, NULL ); // DataPtr + will_return( StubGetVariableNull, EFI_NOT_FOUND ); // Status + PolicyCheck = ValidateSetVariable( TEST_VAR_1_NAME, + &mTestGuid1, + VARIABLE_ATTRIBUTE_BS_RT_AT, + sizeof(DummyData), + DummyData ); + UT_ASSERT_NOT_EFI_ERROR( PolicyCheck ); + + // With a policy, make sure that a call with an "existing" variable fails. + will_return( StubGetVariableNull, TEST_POLICY_ATTRIBUTES_NULL ); // Attributes + will_return( StubGetVariableNull, 10 ); // Size + will_return( StubGetVariableNull, NULL ); // DataPtr + will_return( StubGetVariableNull, EFI_BUFFER_TOO_SMALL ); // Status + PolicyCheck = ValidateSetVariable( TEST_VAR_1_NAME, + &mTestGuid1, + VARIABLE_ATTRIBUTE_BS_RT_AT, + sizeof(DummyData), + DummyData ); + UT_ASSERT_TRUE( EFI_ERROR( PolicyCheck ) ); + + return UNIT_TEST_PASSED; +} + +UNIT_TEST_STATUS +EFIAPI +LockOnStatePoliciesShouldBeHonored ( + IN UNIT_TEST_CONTEXT Context + ) +{ + EXPANDED_VARIABLE_POLICY_ENTRY ValidationPolicy = { + { + VARIABLE_POLICY_ENTRY_REVISION, + 0, // Will be populated by init helper. + 0, // Will be populated by init helper. + TEST_GUID_1, + TEST_POLICY_MIN_SIZE_NULL, + TEST_POLICY_MAX_SIZE_NULL, + TEST_POLICY_ATTRIBUTES_NULL, + TEST_POLICY_ATTRIBUTES_NULL, + VARIABLE_POLICY_TYPE_LOCK_ON_VAR_STATE + }, + { + TEST_GUID_2, + 20, // Value + 0 // Padding + }, + L"", + L"" + }; + EFI_STATUS PolicyCheck; + UINT8 DummyData[12]; + UINT8 ValidationStateVar; + UINTN ExpectedDataSize; + UT_ASSERT_TRUE( InitExpVarPolicyStrings( &ValidationPolicy, TEST_VAR_1_NAME, TEST_VAR_2_NAME ) ); + + + // Without a policy, there should be no constraints on variable creation. + PolicyCheck = ValidateSetVariable( TEST_VAR_1_NAME, + &mTestGuid1, + VARIABLE_ATTRIBUTE_BS_RT_AT, + sizeof(DummyData), + DummyData ); + UT_ASSERT_NOT_EFI_ERROR( PolicyCheck ); + + // Set a policy to test against. + UT_ASSERT_NOT_EFI_ERROR( RegisterVariablePolicy( &ValidationPolicy.Header ) ); + + // Set consistent expectations on what the calls are looking for. + expect_memory_count( StubGetVariableNull, VariableName, TEST_VAR_2_NAME, sizeof(TEST_VAR_2_NAME), 5 ); + expect_memory_count( StubGetVariableNull, VendorGuid, &mTestGuid2, sizeof(mTestGuid2), 5 ); + ExpectedDataSize = 1; + expect_memory_count( StubGetVariableNull, DataSize, &ExpectedDataSize, sizeof(ExpectedDataSize), 5 ); + + // With a policy, make sure that writes still work, since the variable doesn't exist. + will_return( StubGetVariableNull, TEST_POLICY_ATTRIBUTES_NULL ); // Attributes + will_return( StubGetVariableNull, 0 ); // Size + will_return( StubGetVariableNull, NULL ); // DataPtr + will_return( StubGetVariableNull, EFI_NOT_FOUND ); // Status + PolicyCheck = ValidateSetVariable( TEST_VAR_1_NAME, + &mTestGuid1, + VARIABLE_ATTRIBUTE_BS_RT_AT, + sizeof(DummyData), + DummyData ); + UT_ASSERT_NOT_EFI_ERROR( PolicyCheck ); + + // With a policy, make sure that a state variable that's too large doesn't lock the variable. + will_return( StubGetVariableNull, TEST_POLICY_ATTRIBUTES_NULL ); // Attributes + will_return( StubGetVariableNull, 10 ); // Size + will_return( StubGetVariableNull, NULL ); // DataPtr + will_return( StubGetVariableNull, EFI_BUFFER_TOO_SMALL ); // Status + PolicyCheck = ValidateSetVariable( TEST_VAR_1_NAME, + &mTestGuid1, + VARIABLE_ATTRIBUTE_BS_RT_AT, + sizeof(DummyData), + DummyData ); + UT_ASSERT_NOT_EFI_ERROR( PolicyCheck ); + + // With a policy, check a state variable with the wrong value. + ValidationStateVar = 0; + will_return( StubGetVariableNull, TEST_POLICY_ATTRIBUTES_NULL ); // Attributes + will_return( StubGetVariableNull, sizeof(ValidationStateVar) ); // Size + will_return( StubGetVariableNull, &ValidationStateVar ); // DataPtr + will_return( StubGetVariableNull, EFI_SUCCESS ); // Status + PolicyCheck = ValidateSetVariable( TEST_VAR_1_NAME, + &mTestGuid1, + VARIABLE_ATTRIBUTE_BS_RT_AT, + sizeof(DummyData), + DummyData ); + UT_ASSERT_NOT_EFI_ERROR( PolicyCheck ); + + // With a policy, check a state variable with another wrong value. + ValidationStateVar = 10; + will_return( StubGetVariableNull, TEST_POLICY_ATTRIBUTES_NULL ); // Attributes + will_return( StubGetVariableNull, sizeof(ValidationStateVar) ); // Size + will_return( StubGetVariableNull, &ValidationStateVar ); // DataPtr + will_return( StubGetVariableNull, EFI_SUCCESS ); // Status + PolicyCheck = ValidateSetVariable( TEST_VAR_1_NAME, + &mTestGuid1, + VARIABLE_ATTRIBUTE_BS_RT_AT, + sizeof(DummyData), + DummyData ); + UT_ASSERT_NOT_EFI_ERROR( PolicyCheck ); + + // With a policy, make sure that a call with a correct state variable fails. + ValidationStateVar = 20; + will_return( StubGetVariableNull, TEST_POLICY_ATTRIBUTES_NULL ); // Attributes + will_return( StubGetVariableNull, sizeof(ValidationStateVar) ); // Size + will_return( StubGetVariableNull, &ValidationStateVar ); // DataPtr + will_return( StubGetVariableNull, EFI_SUCCESS ); // Status + PolicyCheck = ValidateSetVariable( TEST_VAR_1_NAME, + &mTestGuid1, + VARIABLE_ATTRIBUTE_BS_RT_AT, + sizeof(DummyData), + DummyData ); + UT_ASSERT_TRUE( EFI_ERROR( PolicyCheck ) ); + + return UNIT_TEST_PASSED; +} + +UNIT_TEST_STATUS +EFIAPI +LockOnStatePoliciesShouldApplyToNamespaces ( + IN UNIT_TEST_CONTEXT Context + ) +{ + EXPANDED_VARIABLE_POLICY_ENTRY ValidationPolicy = { + { + VARIABLE_POLICY_ENTRY_REVISION, + 0, // Will be populated by init helper. + 0, // Will be populated by init helper. + TEST_GUID_1, + TEST_POLICY_MIN_SIZE_NULL, + TEST_POLICY_MAX_SIZE_NULL, + TEST_POLICY_ATTRIBUTES_NULL, + TEST_POLICY_ATTRIBUTES_NULL, + VARIABLE_POLICY_TYPE_LOCK_ON_VAR_STATE + }, + { + TEST_GUID_2, + 20, // Value + 0 // Padding + }, + L"", + L"" + }; + EFI_STATUS PolicyCheck; + UINT8 DummyData[12]; + UINT8 ValidationStateVar; + UINTN ExpectedDataSize; + UT_ASSERT_TRUE( InitExpVarPolicyStrings( &ValidationPolicy, NULL, TEST_VAR_2_NAME ) ); + + + // Without a policy, there should be no constraints on variable creation. + PolicyCheck = ValidateSetVariable( TEST_VAR_1_NAME, + &mTestGuid1, + VARIABLE_ATTRIBUTE_BS_RT_AT, + sizeof(DummyData), + DummyData ); + UT_ASSERT_NOT_EFI_ERROR( PolicyCheck ); + PolicyCheck = ValidateSetVariable( TEST_VAR_3_NAME, + &mTestGuid1, + VARIABLE_ATTRIBUTE_BS_RT_AT, + sizeof(DummyData), + DummyData ); + UT_ASSERT_NOT_EFI_ERROR( PolicyCheck ); + + // Set a policy to test against. + UT_ASSERT_NOT_EFI_ERROR( RegisterVariablePolicy( &ValidationPolicy.Header ) ); + + // Set consistent expectations on what the calls are looking for. + expect_memory_count( StubGetVariableNull, VariableName, TEST_VAR_2_NAME, sizeof(TEST_VAR_2_NAME), 4 ); + expect_memory_count( StubGetVariableNull, VendorGuid, &mTestGuid2, sizeof(mTestGuid2), 4 ); + ExpectedDataSize = 1; + expect_memory_count( StubGetVariableNull, DataSize, &ExpectedDataSize, sizeof(ExpectedDataSize), 4 ); + + // With a policy, make sure that writes still work, since the variable doesn't exist. + will_return( StubGetVariableNull, TEST_POLICY_ATTRIBUTES_NULL ); // Attributes + will_return( StubGetVariableNull, 0 ); // Size + will_return( StubGetVariableNull, NULL ); // DataPtr + will_return( StubGetVariableNull, EFI_NOT_FOUND ); // Status + PolicyCheck = ValidateSetVariable( TEST_VAR_1_NAME, + &mTestGuid1, + VARIABLE_ATTRIBUTE_BS_RT_AT, + sizeof(DummyData), + DummyData ); + UT_ASSERT_NOT_EFI_ERROR( PolicyCheck ); + will_return( StubGetVariableNull, TEST_POLICY_ATTRIBUTES_NULL ); // Attributes + will_return( StubGetVariableNull, 0 ); // Size + will_return( StubGetVariableNull, NULL ); // DataPtr + will_return( StubGetVariableNull, EFI_NOT_FOUND ); // Status + PolicyCheck = ValidateSetVariable( TEST_VAR_3_NAME, + &mTestGuid1, + VARIABLE_ATTRIBUTE_BS_RT_AT, + sizeof(DummyData), + DummyData ); + UT_ASSERT_NOT_EFI_ERROR( PolicyCheck ); + + // With a policy, make sure that a call with a correct state variable fails. + ValidationStateVar = 20; + will_return( StubGetVariableNull, TEST_POLICY_ATTRIBUTES_NULL ); // Attributes + will_return( StubGetVariableNull, sizeof(ValidationStateVar) ); // Size + will_return( StubGetVariableNull, &ValidationStateVar ); // DataPtr + will_return( StubGetVariableNull, EFI_SUCCESS ); // Status + PolicyCheck = ValidateSetVariable( TEST_VAR_1_NAME, + &mTestGuid1, + VARIABLE_ATTRIBUTE_BS_RT_AT, + sizeof(DummyData), + DummyData ); + UT_ASSERT_TRUE( EFI_ERROR( PolicyCheck ) ); + will_return( StubGetVariableNull, TEST_POLICY_ATTRIBUTES_NULL ); // Attributes + will_return( StubGetVariableNull, sizeof(ValidationStateVar) ); // Size + will_return( StubGetVariableNull, &ValidationStateVar ); // DataPtr + will_return( StubGetVariableNull, EFI_SUCCESS ); // Status + PolicyCheck = ValidateSetVariable( TEST_VAR_3_NAME, + &mTestGuid1, + VARIABLE_ATTRIBUTE_BS_RT_AT, + sizeof(DummyData), + DummyData ); + UT_ASSERT_TRUE( EFI_ERROR( PolicyCheck ) ); + + return UNIT_TEST_PASSED; +} + +UNIT_TEST_STATUS +EFIAPI +LockOnStateShouldHandleErrorsGracefully ( + IN UNIT_TEST_CONTEXT Context + ) +{ + EXPANDED_VARIABLE_POLICY_ENTRY ValidationPolicy = { + { + VARIABLE_POLICY_ENTRY_REVISION, + 0, // Will be populated by init helper. + 0, // Will be populated by init helper. + TEST_GUID_1, + TEST_POLICY_MIN_SIZE_NULL, + TEST_POLICY_MAX_SIZE_NULL, + TEST_POLICY_ATTRIBUTES_NULL, + TEST_POLICY_ATTRIBUTES_NULL, + VARIABLE_POLICY_TYPE_LOCK_ON_VAR_STATE + }, + { + TEST_GUID_2, + 20, // Value + 0 // Padding + }, + L"", + L"" + }; + EFI_STATUS PolicyCheck; + UINT8 DummyData[12]; + UT_ASSERT_TRUE( InitExpVarPolicyStrings( &ValidationPolicy, TEST_VAR_1_NAME, TEST_VAR_2_NAME ) ); + + + // Without a policy, there should be no constraints on variable creation. + PolicyCheck = ValidateSetVariable( TEST_VAR_1_NAME, + &mTestGuid1, + VARIABLE_ATTRIBUTE_BS_RT_AT, + sizeof(DummyData), + DummyData ); + UT_ASSERT_NOT_EFI_ERROR( PolicyCheck ); + + // Set a policy to test against. + UT_ASSERT_NOT_EFI_ERROR( RegisterVariablePolicy( &ValidationPolicy.Header ) ); + + // Configure the stub to not care about parameters. We're testing errors. + expect_any_always( StubGetVariableNull, VariableName ); + expect_any_always( StubGetVariableNull, VendorGuid ); + expect_any_always( StubGetVariableNull, DataSize ); + + // With a policy, make sure that writes still work, since the variable doesn't exist. + will_return( StubGetVariableNull, TEST_POLICY_ATTRIBUTES_NULL ); // Attributes + will_return( StubGetVariableNull, 0 ); // Size + will_return( StubGetVariableNull, NULL ); // DataPtr + will_return( StubGetVariableNull, EFI_NOT_FOUND ); // Status + PolicyCheck = ValidateSetVariable( TEST_VAR_1_NAME, + &mTestGuid1, + VARIABLE_ATTRIBUTE_BS_RT_AT, + sizeof(DummyData), + DummyData ); + UT_ASSERT_NOT_EFI_ERROR( PolicyCheck ); + + // Verify that state variables that are the wrong size won't lock the variable. + will_return( StubGetVariableNull, TEST_POLICY_ATTRIBUTES_NULL ); // Attributes + will_return( StubGetVariableNull, 0 ); // Size + will_return( StubGetVariableNull, NULL ); // DataPtr + will_return( StubGetVariableNull, EFI_BUFFER_TOO_SMALL ); // Status + PolicyCheck = ValidateSetVariable( TEST_VAR_1_NAME, + &mTestGuid1, + VARIABLE_ATTRIBUTE_BS_RT_AT, + sizeof(DummyData), + DummyData ); + UT_ASSERT_NOT_EFI_ERROR( PolicyCheck ); + + // Verify that unexpected errors default to locked. + will_return( StubGetVariableNull, TEST_POLICY_ATTRIBUTES_NULL ); // Attributes + will_return( StubGetVariableNull, 0 ); // Size + will_return( StubGetVariableNull, NULL ); // DataPtr + will_return( StubGetVariableNull, EFI_UNSUPPORTED ); // Status + PolicyCheck = ValidateSetVariable( TEST_VAR_1_NAME, + &mTestGuid1, + VARIABLE_ATTRIBUTE_BS_RT_AT, + sizeof(DummyData), + DummyData ); + UT_ASSERT_TRUE( EFI_ERROR( PolicyCheck ) ); + + will_return( StubGetVariableNull, TEST_POLICY_ATTRIBUTES_NULL ); // Attributes + will_return( StubGetVariableNull, 0 ); // Size + will_return( StubGetVariableNull, NULL ); // DataPtr + will_return( StubGetVariableNull, EFI_NOT_READY ); // Status + PolicyCheck = ValidateSetVariable( TEST_VAR_1_NAME, + &mTestGuid1, + VARIABLE_ATTRIBUTE_BS_RT_AT, + sizeof(DummyData), + DummyData ); + UT_ASSERT_TRUE( EFI_ERROR( PolicyCheck ) ); + + return UNIT_TEST_PASSED; +} + +UNIT_TEST_STATUS +EFIAPI +BestMatchPriorityShouldBeObeyed ( + IN UNIT_TEST_CONTEXT Context + ) +{ + SIMPLE_VARIABLE_POLICY_ENTRY ValidationPolicy = { + { + VARIABLE_POLICY_ENTRY_REVISION, + sizeof(VARIABLE_POLICY_ENTRY) + sizeof(L"Wild12Card34Placeholder"), + sizeof(VARIABLE_POLICY_ENTRY), + TEST_GUID_1, + TEST_POLICY_MIN_SIZE_NULL, + TEST_POLICY_MAX_SIZE_NULL, + TEST_POLICY_ATTRIBUTES_NULL, + TEST_POLICY_ATTRIBUTES_NULL, + VARIABLE_POLICY_TYPE_NO_LOCK + }, + L"Wild12Card34Placeholder" + }; + EFI_STATUS PolicyCheck; + UINT8 DummyData[70]; + CHAR16 *PolicyName = (CHAR16*)((UINT8*)&ValidationPolicy + sizeof(VARIABLE_POLICY_ENTRY)); + UINTN PolicyNameSize = sizeof(L"Wild12Card34Placeholder"); + CHAR16 *FourWildcards = L"Wild##Card##Placeholder"; + CHAR16 *ThreeWildcards = L"Wild##Card#4Placeholder"; + CHAR16 *TwoWildcards = L"Wild##Card34Placeholder"; + CHAR16 *OneWildcard = L"Wild#2Card34Placeholder"; + CHAR16 *NoWildcards = L"Wild12Card34Placeholder"; + + // Create all of the policies from least restrictive to most restrictive. + // NoWildcards should be the most restrictive. + ValidationPolicy.Header.MaxSize = 60; + ValidationPolicy.Header.Size = ValidationPolicy.Header.OffsetToName; + UT_ASSERT_NOT_EFI_ERROR( RegisterVariablePolicy( &ValidationPolicy.Header ) ); + ValidationPolicy.Header.Size += (UINT16)PolicyNameSize; + ValidationPolicy.Header.MaxSize = 50; + CopyMem( PolicyName, FourWildcards, PolicyNameSize ); + UT_ASSERT_NOT_EFI_ERROR( RegisterVariablePolicy( &ValidationPolicy.Header ) ); + ValidationPolicy.Header.MaxSize = 40; + CopyMem( PolicyName, ThreeWildcards, PolicyNameSize ); + UT_ASSERT_NOT_EFI_ERROR( RegisterVariablePolicy( &ValidationPolicy.Header ) ); + ValidationPolicy.Header.MaxSize = 30; + CopyMem( PolicyName, TwoWildcards, PolicyNameSize ); + UT_ASSERT_NOT_EFI_ERROR( RegisterVariablePolicy( &ValidationPolicy.Header ) ); + ValidationPolicy.Header.MaxSize = 20; + CopyMem( PolicyName, OneWildcard, PolicyNameSize ); + UT_ASSERT_NOT_EFI_ERROR( RegisterVariablePolicy( &ValidationPolicy.Header ) ); + ValidationPolicy.Header.MaxSize = 10; + CopyMem( PolicyName, NoWildcards, PolicyNameSize ); + UT_ASSERT_NOT_EFI_ERROR( RegisterVariablePolicy( &ValidationPolicy.Header ) ); + + // Verify that variables only matching the namespace have the most flexible policy. + PolicyCheck = ValidateSetVariable( L"ArbitraryName", + &mTestGuid1, + VARIABLE_ATTRIBUTE_BS_RT_AT, + 65, + DummyData ); + UT_ASSERT_TRUE( EFI_ERROR( PolicyCheck ) ); + PolicyCheck = ValidateSetVariable( L"ArbitraryName", + &mTestGuid1, + VARIABLE_ATTRIBUTE_BS_RT_AT, + 55, + DummyData ); + UT_ASSERT_NOT_EFI_ERROR( PolicyCheck ); + + // Verify that variables matching increasing characters get increasing policy restrictions. + PolicyCheck = ValidateSetVariable( L"Wild77Card77Placeholder", + &mTestGuid1, + VARIABLE_ATTRIBUTE_BS_RT_AT, + 55, + DummyData ); + UT_ASSERT_TRUE( EFI_ERROR( PolicyCheck ) ); + PolicyCheck = ValidateSetVariable( L"Wild77Card77Placeholder", + &mTestGuid1, + VARIABLE_ATTRIBUTE_BS_RT_AT, + 45, + DummyData ); + UT_ASSERT_NOT_EFI_ERROR( PolicyCheck ); + + PolicyCheck = ValidateSetVariable( L"Wild77Card74Placeholder", + &mTestGuid1, + VARIABLE_ATTRIBUTE_BS_RT_AT, + 45, + DummyData ); + UT_ASSERT_TRUE( EFI_ERROR( PolicyCheck ) ); + PolicyCheck = ValidateSetVariable( L"Wild77Card74Placeholder", + &mTestGuid1, + VARIABLE_ATTRIBUTE_BS_RT_AT, + 35, + DummyData ); + UT_ASSERT_NOT_EFI_ERROR( PolicyCheck ); + + PolicyCheck = ValidateSetVariable( L"Wild77Card34Placeholder", + &mTestGuid1, + VARIABLE_ATTRIBUTE_BS_RT_AT, + 35, + DummyData ); + UT_ASSERT_TRUE( EFI_ERROR( PolicyCheck ) ); + PolicyCheck = ValidateSetVariable( L"Wild77Card34Placeholder", + &mTestGuid1, + VARIABLE_ATTRIBUTE_BS_RT_AT, + 25, + DummyData ); + UT_ASSERT_NOT_EFI_ERROR( PolicyCheck ); + + PolicyCheck = ValidateSetVariable( L"Wild72Card34Placeholder", + &mTestGuid1, + VARIABLE_ATTRIBUTE_BS_RT_AT, + 25, + DummyData ); + UT_ASSERT_TRUE( EFI_ERROR( PolicyCheck ) ); + PolicyCheck = ValidateSetVariable( L"Wild72Card34Placeholder", + &mTestGuid1, + VARIABLE_ATTRIBUTE_BS_RT_AT, + 15, + DummyData ); + UT_ASSERT_NOT_EFI_ERROR( PolicyCheck ); + + PolicyCheck = ValidateSetVariable( L"Wild12Card34Placeholder", + &mTestGuid1, + VARIABLE_ATTRIBUTE_BS_RT_AT, + 15, + DummyData ); + UT_ASSERT_TRUE( EFI_ERROR( PolicyCheck ) ); + PolicyCheck = ValidateSetVariable( L"Wild12Card34Placeholder", + &mTestGuid1, + VARIABLE_ATTRIBUTE_BS_RT_AT, + 5, + DummyData ); + UT_ASSERT_NOT_EFI_ERROR( PolicyCheck ); + + return UNIT_TEST_PASSED; +} + + +///=== POLICY UTILITY SUITE =================================================== + +UNIT_TEST_STATUS +EFIAPI +ShouldBeAbleToLockInterface ( + IN UNIT_TEST_CONTEXT Context + ) +{ + SIMPLE_VARIABLE_POLICY_ENTRY TestPolicy = { + { + VARIABLE_POLICY_ENTRY_REVISION, + sizeof(VARIABLE_POLICY_ENTRY) + sizeof(TEST_VAR_1_NAME), + sizeof(VARIABLE_POLICY_ENTRY), + TEST_GUID_1, + TEST_POLICY_MIN_SIZE_NULL, + TEST_POLICY_MAX_SIZE_NULL, + TEST_POLICY_ATTRIBUTES_NULL, + TEST_POLICY_ATTRIBUTES_NULL, + VARIABLE_POLICY_TYPE_NO_LOCK + }, + TEST_VAR_1_NAME + }; + + // Make sure it's not already locked. + UT_ASSERT_FALSE( IsVariablePolicyInterfaceLocked() ); + // Lock it. + UT_ASSERT_NOT_EFI_ERROR( LockVariablePolicy() ); + // Verify that it's locked. + UT_ASSERT_TRUE( IsVariablePolicyInterfaceLocked() ); + + // Verify that all state-changing commands fail. + UT_ASSERT_TRUE( EFI_ERROR( LockVariablePolicy() ) ); + UT_ASSERT_TRUE( EFI_ERROR( DisableVariablePolicy() ) ); + UT_ASSERT_TRUE( EFI_ERROR( RegisterVariablePolicy( &TestPolicy.Header ) ) ); + + return UNIT_TEST_PASSED; +} + +UNIT_TEST_STATUS +EFIAPI +ShouldBeAbleToDisablePolicyEnforcement ( + IN UNIT_TEST_CONTEXT Context + ) +{ + SIMPLE_VARIABLE_POLICY_ENTRY TestPolicy = { + { + VARIABLE_POLICY_ENTRY_REVISION, + sizeof(VARIABLE_POLICY_ENTRY) + sizeof(TEST_VAR_1_NAME), + sizeof(VARIABLE_POLICY_ENTRY), + TEST_GUID_1, + TEST_POLICY_MIN_SIZE_10, + TEST_POLICY_MAX_SIZE_200, + TEST_POLICY_ATTRIBUTES_NULL, + TEST_POLICY_ATTRIBUTES_NULL, + VARIABLE_POLICY_TYPE_NO_LOCK + }, + TEST_VAR_1_NAME + }; + EFI_STATUS PolicyCheck; + UINT8 DummyData[TEST_POLICY_MIN_SIZE_10-1]; + + // Make sure that the policy enforcement is currently enabled. + UT_ASSERT_TRUE( IsVariablePolicyEnabled() ); + // Add a policy before it's disabled. + UT_ASSERT_NOT_EFI_ERROR( RegisterVariablePolicy( &TestPolicy.Header ) ); + // Disable the policy enforcement. + UT_ASSERT_NOT_EFI_ERROR( DisableVariablePolicy() ); + // Make sure that the policy enforcement is currently disabled. + UT_ASSERT_FALSE( IsVariablePolicyEnabled() ); + + // Check to make sure that a policy violation still passes. + PolicyCheck = ValidateSetVariable( TEST_VAR_1_NAME, + &mTestGuid1, + VARIABLE_ATTRIBUTE_NV_BS, + sizeof(DummyData), + DummyData ); + UT_ASSERT_NOT_EFI_ERROR( PolicyCheck ); + + return UNIT_TEST_PASSED; +} + +UNIT_TEST_STATUS +EFIAPI +ShouldNotBeAbleToDisablePoliciesTwice ( + IN UNIT_TEST_CONTEXT Context + ) +{ + // Make sure that the policy enforcement is currently enabled. + UT_ASSERT_TRUE( IsVariablePolicyEnabled() ); + // Disable the policy enforcement. + UT_ASSERT_NOT_EFI_ERROR( DisableVariablePolicy() ); + // Make sure that the policy enforcement is currently disabled. + UT_ASSERT_FALSE( IsVariablePolicyEnabled() ); + // Try to disable again and verify failure. + UT_ASSERT_TRUE( EFI_ERROR( DisableVariablePolicy() ) ); + + return UNIT_TEST_PASSED; +} + +UNIT_TEST_STATUS +EFIAPI +ShouldBeAbleToAddNewPoliciesAfterDisabled ( + IN UNIT_TEST_CONTEXT Context + ) +{ + SIMPLE_VARIABLE_POLICY_ENTRY TestPolicy = { + { + VARIABLE_POLICY_ENTRY_REVISION, + sizeof(VARIABLE_POLICY_ENTRY) + sizeof(TEST_VAR_1_NAME), + sizeof(VARIABLE_POLICY_ENTRY), + TEST_GUID_1, + TEST_POLICY_MIN_SIZE_10, + TEST_POLICY_MAX_SIZE_200, + TEST_POLICY_ATTRIBUTES_NULL, + TEST_POLICY_ATTRIBUTES_NULL, + VARIABLE_POLICY_TYPE_NO_LOCK + }, + TEST_VAR_1_NAME + }; + EFI_STATUS PolicyCheck; + + // Make sure that the policy enforcement is currently enabled. + UT_ASSERT_TRUE( IsVariablePolicyEnabled() ); + // Disable the policy enforcement. + UT_ASSERT_NOT_EFI_ERROR( DisableVariablePolicy() ); + + // Make sure that new policy creation still works, it just won't be enforced. + PolicyCheck = RegisterVariablePolicy( &TestPolicy.Header ); + UT_ASSERT_NOT_EFI_ERROR( PolicyCheck ); + + return UNIT_TEST_PASSED; +} + +UNIT_TEST_STATUS +EFIAPI +ShouldBeAbleToLockAfterDisabled ( + IN UNIT_TEST_CONTEXT Context + ) +{ + // Make sure that the policy enforcement is currently enabled. + UT_ASSERT_TRUE( IsVariablePolicyEnabled() ); + // Disable the policy enforcement. + UT_ASSERT_NOT_EFI_ERROR( DisableVariablePolicy() ); + + // Make sure that we can lock in this state. + UT_ASSERT_FALSE( IsVariablePolicyInterfaceLocked() ); + UT_ASSERT_NOT_EFI_ERROR( LockVariablePolicy() ); + UT_ASSERT_TRUE( IsVariablePolicyInterfaceLocked() ); + + return UNIT_TEST_PASSED; +} + +UNIT_TEST_STATUS +EFIAPI +ShouldBeAbleToDumpThePolicyTable ( + IN UNIT_TEST_CONTEXT Context + ) +{ + SIMPLE_VARIABLE_POLICY_ENTRY TestPolicy = { + { + VARIABLE_POLICY_ENTRY_REVISION, + sizeof(VARIABLE_POLICY_ENTRY) + sizeof(TEST_VAR_1_NAME), + sizeof(VARIABLE_POLICY_ENTRY), + TEST_GUID_1, + TEST_POLICY_MIN_SIZE_10, + TEST_POLICY_MAX_SIZE_200, + TEST_POLICY_ATTRIBUTES_NULL, + TEST_POLICY_ATTRIBUTES_NULL, + VARIABLE_POLICY_TYPE_NO_LOCK + }, + TEST_VAR_1_NAME + }; + EFI_STATUS PolicyCheck; + UINT32 DumpSize; + UINT32 BufferSize; + VOID *DumpBuffer; + + // For good measure, test some parameter validation. + UT_ASSERT_STATUS_EQUAL( DumpVariablePolicy( NULL, NULL ), EFI_INVALID_PARAMETER ); + DumpSize = 10; + UT_ASSERT_STATUS_EQUAL( DumpVariablePolicy( NULL, &DumpSize ), EFI_INVALID_PARAMETER ); + + // Now for the actual test case. + + // Allocate a buffer to hold the output. + BufferSize = sizeof(VARIABLE_POLICY_ENTRY) + sizeof(TEST_VAR_1_NAME); + DumpBuffer = AllocatePool( BufferSize ); + UT_ASSERT_NOT_EQUAL( DumpBuffer, NULL ); + + // Verify that the current table size is 0. + DumpSize = BufferSize; + UT_ASSERT_NOT_EFI_ERROR( DumpVariablePolicy( DumpBuffer, &DumpSize ) ); + UT_ASSERT_EQUAL( DumpSize, 0 ); + + // Now, set a new policy. + UT_ASSERT_NOT_EFI_ERROR( RegisterVariablePolicy( &TestPolicy.Header ) ); + + // Make sure that the new return is non-zero and fails as expected. + DumpSize = 0; + PolicyCheck = DumpVariablePolicy( NULL, &DumpSize ); + UT_ASSERT_STATUS_EQUAL( PolicyCheck, EFI_BUFFER_TOO_SMALL ); + UT_ASSERT_EQUAL( DumpSize, BufferSize ); + + // Now verify that we can fetch the dump. + DumpSize = BufferSize; + UT_ASSERT_NOT_EFI_ERROR( DumpVariablePolicy( DumpBuffer, &DumpSize ) ); + UT_ASSERT_EQUAL( DumpSize, BufferSize ); + UT_ASSERT_MEM_EQUAL( &TestPolicy, DumpBuffer, BufferSize ); + + // Always put away your toys. + FreePool( DumpBuffer ); + + return UNIT_TEST_PASSED; +} + +UNIT_TEST_STATUS +EFIAPI +ShouldBeAbleToDumpThePolicyTableAfterDisabled ( + IN UNIT_TEST_CONTEXT Context + ) +{ + SIMPLE_VARIABLE_POLICY_ENTRY TestPolicy = { + { + VARIABLE_POLICY_ENTRY_REVISION, + sizeof(VARIABLE_POLICY_ENTRY) + sizeof(TEST_VAR_1_NAME), + sizeof(VARIABLE_POLICY_ENTRY), + TEST_GUID_1, + TEST_POLICY_MIN_SIZE_10, + TEST_POLICY_MAX_SIZE_200, + TEST_POLICY_ATTRIBUTES_NULL, + TEST_POLICY_ATTRIBUTES_NULL, + VARIABLE_POLICY_TYPE_NO_LOCK + }, + TEST_VAR_1_NAME + }; + SIMPLE_VARIABLE_POLICY_ENTRY TestPolicy2 = { + { + VARIABLE_POLICY_ENTRY_REVISION, + sizeof(VARIABLE_POLICY_ENTRY) + sizeof(TEST_VAR_2_NAME), + sizeof(VARIABLE_POLICY_ENTRY), + TEST_GUID_2, + TEST_POLICY_MIN_SIZE_10, + TEST_POLICY_MAX_SIZE_200, + TEST_POLICY_ATTRIBUTES_NULL, + TEST_POLICY_ATTRIBUTES_NULL, + VARIABLE_POLICY_TYPE_NO_LOCK + }, + TEST_VAR_2_NAME + }; + EFI_STATUS PolicyCheck; + UINT32 DumpSize; + VOID *DumpBuffer; + + DumpBuffer = NULL; + DumpSize = 0; + + // Register a new policy. + UT_ASSERT_NOT_EFI_ERROR( RegisterVariablePolicy( &TestPolicy.Header ) ); + // Make sure that we can dump the policy. + PolicyCheck = DumpVariablePolicy( DumpBuffer, &DumpSize ); + UT_ASSERT_STATUS_EQUAL( PolicyCheck, EFI_BUFFER_TOO_SMALL ); + DumpBuffer = AllocatePool( DumpSize ); + UT_ASSERT_NOT_EFI_ERROR( DumpVariablePolicy( DumpBuffer, &DumpSize ) ); + UT_ASSERT_MEM_EQUAL( DumpBuffer, &TestPolicy, DumpSize ); + + // Clean up from this step. + FreePool( DumpBuffer ); + DumpBuffer = NULL; + DumpSize = 0; + + // Now disable the engine. + DisableVariablePolicy(); + + // Now register a new policy and make sure that both can be dumped. + UT_ASSERT_NOT_EFI_ERROR( RegisterVariablePolicy( &TestPolicy2.Header ) ); + // Make sure that we can dump the policy. + PolicyCheck = DumpVariablePolicy( DumpBuffer, &DumpSize ); + UT_ASSERT_STATUS_EQUAL( PolicyCheck, EFI_BUFFER_TOO_SMALL ); + DumpBuffer = AllocatePool( DumpSize ); + UT_ASSERT_NOT_EFI_ERROR( DumpVariablePolicy( DumpBuffer, &DumpSize ) ); + + // Finally, make sure that both policies are in the dump. + UT_ASSERT_MEM_EQUAL( DumpBuffer, &TestPolicy, TestPolicy.Header.Size ); + UT_ASSERT_MEM_EQUAL( (UINT8*)DumpBuffer + TestPolicy.Header.Size, + &TestPolicy2, + TestPolicy2.Header.Size ); + + // Always put away your toys. + FreePool( DumpBuffer ); + + return UNIT_TEST_PASSED; +} + + +///=== TEST ENGINE ================================================================================ + +/** + SampleUnitTestApp + + @param[in] ImageHandle The firmware allocated handle for the EFI image. + @param[in] SystemTable A pointer to the EFI System Table. + + @retval EFI_SUCCESS The entry point executed successfully. + @retval other Some error occured when executing this entry point. + +**/ +int main () +{ + EFI_STATUS Status; + UNIT_TEST_FRAMEWORK_HANDLE Framework = NULL; + UNIT_TEST_SUITE_HANDLE ArchTests; + UNIT_TEST_SUITE_HANDLE PolicyTests; + UNIT_TEST_SUITE_HANDLE UtilityTests; +#ifdef INTERNAL_UNIT_TEST + UNIT_TEST_SUITE_HANDLE InternalTests; +#endif // INTERNAL_UNIT_TEST + + DEBUG(( DEBUG_INFO, "%a v%a\n", UNIT_TEST_NAME, UNIT_TEST_VERSION )); + + // + // Start setting up the test framework for running the tests. + // + Status = InitUnitTestFramework( &Framework, UNIT_TEST_NAME, gEfiCallerBaseName, UNIT_TEST_VERSION ); + if (EFI_ERROR( Status )) + { + DEBUG((DEBUG_ERROR, "Failed in InitUnitTestFramework. Status = %r\n", Status)); + goto EXIT; + } + + + // + // Add all test suites and tests. + // + Status = CreateUnitTestSuite( &ArchTests, Framework, "Variable Policy Architectural Tests", "VarPolicy.Arch", NULL, NULL ); + if (EFI_ERROR( Status )) + { + DEBUG((DEBUG_ERROR, "Failed in CreateUnitTestSuite for ArchTests\n")); + Status = EFI_OUT_OF_RESOURCES; + goto EXIT; + } + AddTestCase( ArchTests, + "Deinitialization should fail if not previously initialized", "VarPolicy.Arch.OnlyDeinit", + ShouldFailDeinitWithoutInit, NULL, NULL, NULL ); + AddTestCase( ArchTests, + "Initialization followed by deinitialization should succeed", "VarPolicy.Arch.InitDeinit", + ShouldBeAbleToInitAndDeinitTheLibrary, NULL, NULL, NULL ); + AddTestCase( ArchTests, + "The initialization function fail if called twice without a deinit", "VarPolicy.Arch.InitTwice", + ShouldNotBeAbleToInitializeTheLibraryTwice, NULL, LibCleanup, NULL ); + AddTestCase( ArchTests, + "API functions should be unavailable until library is initialized", "VarPolicy.Arch.UninitApiOff", + ApiCommandsShouldNotRespondIfLibIsUninitialized, NULL, LibCleanup, NULL ); + +#ifdef INTERNAL_UNIT_TEST + Status = CreateUnitTestSuite( &InternalTests, Framework, "Variable Policy Internal Tests", "VarPolicy.Internal", NULL, NULL ); + if (EFI_ERROR( Status )) + { + DEBUG((DEBUG_ERROR, "Failed in CreateUnitTestSuite for InternalTests\n")); + Status = EFI_OUT_OF_RESOURCES; + goto EXIT; + } + AddTestCase( InternalTests, + "Policy matching should use name and GUID", "VarPolicy.Internal.NameGuid", + PoliciesShouldMatchByNameAndGuid, LibInitMocked, LibCleanup, NULL ); + AddTestCase( InternalTests, + "# sign wildcards should match digits", "VarPolicy.Internal.WildDigits", + WildcardPoliciesShouldMatchDigits, LibInitMocked, LibCleanup, NULL ); + AddTestCase( InternalTests, + "Digit wildcards should check edge cases", "VarPolicy.Internal.WildDigitsAdvanced", + WildcardPoliciesShouldMatchDigitsAdvanced, LibInitMocked, LibCleanup, NULL ); + AddTestCase( InternalTests, + "Empty names should match an entire namespace", "VarPolicy.Internal.WildNamespace", + WildcardPoliciesShouldMatchNamespaces, LibInitMocked, LibCleanup, NULL ); + AddTestCase( InternalTests, + "Match priority should weight correctly based on wildcards", "VarPolicy.Internal.Priorities", + MatchPrioritiesShouldFollowRules, LibInitMocked, LibCleanup, NULL ); +#endif // INTERNAL_UNIT_TEST + + Status = CreateUnitTestSuite( &PolicyTests, Framework, "Variable Policy Manipulation Tests", "VarPolicy.Policy", NULL, NULL ); + if (EFI_ERROR( Status )) + { + DEBUG((DEBUG_ERROR, "Failed in CreateUnitTestSuite for PolicyTests\n")); + Status = EFI_OUT_OF_RESOURCES; + goto EXIT; + } + AddTestCase( PolicyTests, + "RegisterShouldAllowNamespaceWildcards", "VarPolicy.Policy.AllowNamespace", + RegisterShouldAllowNamespaceWildcards, LibInitMocked, LibCleanup, NULL ); + AddTestCase( PolicyTests, + "RegisterShouldAllowStateVarsForNamespaces", "VarPolicy.Policy.AllowStateNamespace", + RegisterShouldAllowStateVarsForNamespaces, LibInitMocked, LibCleanup, NULL ); + AddTestCase( PolicyTests, + "RegisterShouldRejectNullPointers", "VarPolicy.Policy.NullPointers", + RegisterShouldRejectNullPointers, LibInitMocked, LibCleanup, NULL ); + AddTestCase( PolicyTests, + "RegisterShouldRejectBadRevisions", "VarPolicy.Policy.BadRevisions", + RegisterShouldRejectBadRevisions, LibInitMocked, LibCleanup, NULL ); + AddTestCase( PolicyTests, + "RegisterShouldRejectBadSizes", "VarPolicy.Policy.BadSizes", + RegisterShouldRejectBadSizes, LibInitMocked, LibCleanup, NULL ); + AddTestCase( PolicyTests, + "RegisterShouldRejectBadOffsets", "VarPolicy.Policy.BadOffsets", + RegisterShouldRejectBadOffsets, LibInitMocked, LibCleanup, NULL ); + AddTestCase( PolicyTests, + "RegisterShouldRejectMissingStateStrings", "VarPolicy.Policy.MissingStateString", + RegisterShouldRejectMissingStateStrings, LibInitMocked, LibCleanup, NULL ); + AddTestCase( PolicyTests, + "RegisterShouldRejectStringsMissingNull", "VarPolicy.Policy.MissingNull", + RegisterShouldRejectStringsMissingNull, LibInitMocked, LibCleanup, NULL ); + AddTestCase( PolicyTests, + "RegisterShouldRejectMalformedStrings", "VarPolicy.Policy.MalformedStrings", + RegisterShouldRejectMalformedStrings, LibInitMocked, LibCleanup, NULL ); + AddTestCase( PolicyTests, + "RegisterShouldRejectUnpackedPolicies", "VarPolicy.Policy.PolicyPacking", + RegisterShouldRejectUnpackedPolicies, LibInitMocked, LibCleanup, NULL ); + AddTestCase( PolicyTests, + "RegisterShouldRejectInvalidNameCharacters", "VarPolicy.Policy.InvalidCharacters", + RegisterShouldRejectInvalidNameCharacters, LibInitMocked, LibCleanup, NULL ); + AddTestCase( PolicyTests, + "RegisterShouldRejectBadPolicyConstraints", "VarPolicy.Policy.BadConstraints", + RegisterShouldRejectBadPolicyConstraints, LibInitMocked, LibCleanup, NULL ); + AddTestCase( PolicyTests, + "RegisterShouldRejectUnknownLockPolicies", "VarPolicy.Policy.BadLocks", + RegisterShouldRejectUnknownLockPolicies, LibInitMocked, LibCleanup, NULL ); + AddTestCase( PolicyTests, + "RegisterShouldRejectPolicesWithTooManyWildcards", "VarPolicy.Policy.TooManyWildcards", + RegisterShouldRejectPolicesWithTooManyWildcards, LibInitMocked, LibCleanup, NULL ); + AddTestCase( PolicyTests, + "RegisterShouldRejectDuplicatePolicies", "VarPolicy.Policy.DuplicatePolicies", + RegisterShouldRejectDuplicatePolicies, LibInitMocked, LibCleanup, NULL ); + AddTestCase( PolicyTests, + "Variables that exceed min or max sizes should be rejected", "VarPolicy.Policy.MinMax", + MinAndMaxSizePoliciesShouldBeHonored, LibInitMocked, LibCleanup, NULL ); + AddTestCase( PolicyTests, + "AttributeMustPoliciesShouldBeHonored", "VarPolicy.Policy.AttrMust", + AttributeMustPoliciesShouldBeHonored, LibInitMocked, LibCleanup, NULL ); + AddTestCase( PolicyTests, + "AttributeCantPoliciesShouldBeHonored", "VarPolicy.Policy.AttrCant", + AttributeCantPoliciesShouldBeHonored, LibInitMocked, LibCleanup, NULL ); + AddTestCase( PolicyTests, + "VariablesShouldBeDeletableRegardlessOfSize", "VarPolicy.Policy.DeleteIgnoreSize", + VariablesShouldBeDeletableRegardlessOfSize, LibInitMocked, LibCleanup, NULL ); + AddTestCase( PolicyTests, + "LockNowPoliciesShouldBeHonored", "VarPolicy.Policy.VARIABLE_POLICY_TYPE_LOCK_NOW", + LockNowPoliciesShouldBeHonored, LibInitMocked, LibCleanup, NULL ); + AddTestCase( PolicyTests, + "LockOnCreatePoliciesShouldBeHonored", "VarPolicy.Policy.VARIABLE_POLICY_TYPE_LOCK_ON_CREATE", + LockOnCreatePoliciesShouldBeHonored, LibInitMocked, LibCleanup, NULL ); + AddTestCase( PolicyTests, + "LockOnStatePoliciesShouldBeHonored", "VarPolicy.Policy.LockState", + LockOnStatePoliciesShouldBeHonored, LibInitMocked, LibCleanup, NULL ); + AddTestCase( PolicyTests, + "LockOnStatePoliciesShouldApplyToNamespaces", "VarPolicy.Policy.NamespaceLockState", + LockOnStatePoliciesShouldApplyToNamespaces, LibInitMocked, LibCleanup, NULL ); + AddTestCase( PolicyTests, + "LockOnStateShouldHandleErrorsGracefully", "VarPolicy.Policy.LockStateErrors", + LockOnStateShouldHandleErrorsGracefully, LibInitMocked, LibCleanup, NULL ); + AddTestCase( PolicyTests, + "BestMatchPriorityShouldBeObeyed", "VarPolicy.Policy.BestMatch", + BestMatchPriorityShouldBeObeyed, LibInitMocked, LibCleanup, NULL ); + + Status = CreateUnitTestSuite( &UtilityTests, Framework, "Variable Policy Utility Tests", "VarPolicy.Utility", NULL, NULL ); + if (EFI_ERROR( Status )) + { + DEBUG((DEBUG_ERROR, "Failed in CreateUnitTestSuite for UtilityTests\n")); + Status = EFI_OUT_OF_RESOURCES; + goto EXIT; + } + AddTestCase( UtilityTests, + "API commands that change state should not respond after interface is locked", "VarPolicy.Utility.InterfaceLock", + ShouldBeAbleToLockInterface, LibInitMocked, LibCleanup, NULL ); + AddTestCase( UtilityTests, + "All policies should pass once enforcement is disabled", "VarPolicy.Utility.DisableEnforcement", + ShouldBeAbleToDisablePolicyEnforcement, LibInitMocked, LibCleanup, NULL ); + AddTestCase( UtilityTests, + "Disabling enforcement twice should produce an error", "VarPolicy.Utility.DisableEnforcementTwice", + ShouldNotBeAbleToDisablePoliciesTwice, LibInitMocked, LibCleanup, NULL ); + AddTestCase( UtilityTests, + "ShouldBeAbleToAddNewPoliciesAfterDisabled", "VarPolicy.Utility.AddAfterDisable", + ShouldBeAbleToAddNewPoliciesAfterDisabled, LibInitMocked, LibCleanup, NULL ); + AddTestCase( UtilityTests, + "ShouldBeAbleToLockAfterDisabled", "VarPolicy.Utility.LockAfterDisable", + ShouldBeAbleToLockAfterDisabled, LibInitMocked, LibCleanup, NULL ); + AddTestCase( UtilityTests, + "Should be able to dump the policy table", "VarPolicy.Utility.DumpTable", + ShouldBeAbleToDumpThePolicyTable, LibInitMocked, LibCleanup, NULL ); + AddTestCase( UtilityTests, + "ShouldBeAbleToDumpThePolicyTableAfterDisabled", "VarPolicy.Utility.DumpTableAfterDisable", + ShouldBeAbleToDumpThePolicyTableAfterDisabled, LibInitMocked, LibCleanup, NULL ); + + + // + // Execute the tests. + // + Status = RunAllTestSuites( Framework ); + +EXIT: + if (Framework) + { + FreeUnitTestFramework( Framework ); + } + + return Status; +} diff --git a/MdeModulePkg/Include/Library/VariablePolicyLib.h b/MdeModulePkg/Include/Library/VariablePolicyLib.h new file mode 100644 index 000000000000..6be16fdd8f24 --- /dev/null +++ b/MdeModulePkg/Include/Library/VariablePolicyLib.h @@ -0,0 +1,206 @@ +/** @file -- VariablePolicyLib.h +Business logic for Variable Policy enforcement. + +Copyright (c) Microsoft Corporation. +SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef _VARIABLE_POLICY_LIB_H_ +#define _VARIABLE_POLICY_LIB_H_ + +#include <Protocol/VariablePolicy.h> + +/** + This API function validates and registers a new policy with + the policy enforcement engine. + + @param[in] NewPolicy Pointer to the incoming policy structure. + + @retval EFI_SUCCESS + @retval EFI_INVALID_PARAMETER NewPolicy is NULL or is internally inconsistent. + @retval EFI_ALREADY_STARTED An identical matching policy already exists. + @retval EFI_WRITE_PROTECTED The interface has been locked until the next reboot. + @retval EFI_UNSUPPORTED Policy enforcement has been disabled. No reason to add more policies. + @retval EFI_ABORTED A calculation error has prevented this function from completing. + @retval EFI_OUT_OF_RESOURCES Cannot grow the table to hold any more policies. + @retval EFI_NOT_READY Library has not yet been initialized. + +**/ +EFI_STATUS +EFIAPI +RegisterVariablePolicy ( + IN CONST VARIABLE_POLICY_ENTRY *NewPolicy + ); + + +/** + This API function checks to see whether the parameters to SetVariable would + be allowed according to the current variable policies. + + @param[in] VariableName Same as EFI_SET_VARIABLE. + @param[in] VendorGuid Same as EFI_SET_VARIABLE. + @param[in] Attributes Same as EFI_SET_VARIABLE. + @param[in] DataSize Same as EFI_SET_VARIABLE. + @param[in] Data Same as EFI_SET_VARIABLE. + + @retval EFI_SUCCESS A matching policy allows this update. + @retval EFI_SUCCESS There are currently no policies that restrict this update. + @retval EFI_SUCCESS The protections have been disable until the next reboot. + @retval EFI_WRITE_PROTECTED Variable is currently locked. + @retval EFI_INVALID_PARAMETER Attributes or size are invalid. + @retval EFI_ABORTED A lock policy exists, but an error prevented evaluation. + @retval EFI_NOT_READY Library has not been initialized. + +**/ +EFI_STATUS +EFIAPI +ValidateSetVariable ( + IN CHAR16 *VariableName, + IN EFI_GUID *VendorGuid, + IN UINT32 Attributes, + IN UINTN DataSize, + IN VOID *Data + ); + + +/** + This API function disables the variable policy enforcement. If it's + already been called once, will return EFI_ALREADY_STARTED. + + @retval EFI_SUCCESS + @retval EFI_ALREADY_STARTED Has already been called once this boot. + @retval EFI_WRITE_PROTECTED Interface has been locked until reboot. + @retval EFI_NOT_READY Library has not yet been initialized. + +**/ +EFI_STATUS +EFIAPI +DisableVariablePolicy ( + VOID + ); + + +/** + This API function will dump the entire contents of the variable policy table. + + Similar to GetVariable, the first call can be made with a 0 size and it will return + the size of the buffer required to hold the entire table. + + @param[out] Policy Pointer to the policy buffer. Can be NULL if Size is 0. + @param[in,out] Size On input, the size of the output buffer. On output, the size + of the data returned. + + @retval EFI_SUCCESS Policy data is in the output buffer and Size has been updated. + @retval EFI_INVALID_PARAMETER Size is NULL, or Size is non-zero and Policy is NULL. + @retval EFI_BUFFER_TOO_SMALL Size is insufficient to hold policy. Size updated with required size. + @retval EFI_NOT_READY Library has not yet been initialized. + +**/ +EFI_STATUS +EFIAPI +DumpVariablePolicy ( + OUT UINT8 *Policy, + IN OUT UINT32 *Size + ); + + +/** + This API function returns whether or not the policy engine is + currently being enforced. + + @retval TRUE + @retval FALSE + @retval FALSE Library has not yet been initialized. + +**/ +BOOLEAN +EFIAPI +IsVariablePolicyEnabled ( + VOID + ); + + +/** + This API function locks the interface so that no more policy updates + can be performed or changes made to the enforcement until the next boot. + + @retval EFI_SUCCESS + @retval EFI_NOT_READY Library has not yet been initialized. + +**/ +EFI_STATUS +EFIAPI +LockVariablePolicy ( + VOID + ); + + +/** + This API function returns whether or not the policy interface is locked + for the remainder of the boot. + + @retval TRUE + @retval FALSE + @retval FALSE Library has not yet been initialized. + +**/ +BOOLEAN +EFIAPI +IsVariablePolicyInterfaceLocked ( + VOID + ); + + +/** + This helper function initializes the library and sets + up any required internal structures or handlers. + + Also registers the internal pointer for the GetVariable helper. + + @param[in] GetVariableHelper A function pointer matching the EFI_GET_VARIABLE prototype that will be used to + check policy criteria that involve the existence of other variables. + + @retval EFI_SUCCESS + @retval EFI_ALREADY_STARTED The initialize function has been called more than once without a call to + deinitialize. + +**/ +EFI_STATUS +EFIAPI +InitVariablePolicyLib ( + IN EFI_GET_VARIABLE GetVariableHelper + ); + + +/** + This helper function returns whether or not the library is currently initialized. + + @retval TRUE + @retval FALSE + +**/ +BOOLEAN +EFIAPI +IsVariablePolicyLibInitialized ( + VOID + ); + + +/** + This helper function tears down the library. + + Should generally only be used for test harnesses. + + @retval EFI_SUCCESS + @retval EFI_NOT_READY Deinitialize was called without first calling initialize. + +**/ +EFI_STATUS +EFIAPI +DeinitVariablePolicyLib ( + VOID + ); + + +#endif // _VARIABLE_POLICY_LIB_H_ diff --git a/MdeModulePkg/Library/VariablePolicyLib/VariablePolicyLib.inf b/MdeModulePkg/Library/VariablePolicyLib/VariablePolicyLib.inf new file mode 100644 index 000000000000..340d5e8793fe --- /dev/null +++ b/MdeModulePkg/Library/VariablePolicyLib/VariablePolicyLib.inf @@ -0,0 +1,38 @@ +## @file VariablePolicyLib.inf +# Business logic for Variable Policy enforcement. +# +## +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: BSD-2-Clause-Patent +## + + +[Defines] + INF_VERSION = 0x00010017 + BASE_NAME = VariablePolicyLib + FILE_GUID = E9ECD342-159A-4F24-9FDF-65724027C594 + VERSION_STRING = 1.0 + MODULE_TYPE = BASE + LIBRARY_CLASS = VariablePolicyLib + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = ANY +# + + +[Sources] + VariablePolicyLib.c + + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + + +[LibraryClasses] + DebugLib + BaseMemoryLib + MemoryAllocationLib + SafeIntLib diff --git a/MdeModulePkg/Library/VariablePolicyLib/VariablePolicyLib.uni b/MdeModulePkg/Library/VariablePolicyLib/VariablePolicyLib.uni new file mode 100644 index 000000000000..2227ec427828 --- /dev/null +++ b/MdeModulePkg/Library/VariablePolicyLib/VariablePolicyLib.uni @@ -0,0 +1,12 @@ +// /** @file +// VariablePolicyLib.uni +// +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: BSD-2-Clause-Patent +// +// **/ + + +#string STR_MODULE_ABSTRACT #language en-US "Library containing the business logic for the VariablePolicy engine" + +#string STR_MODULE_DESCRIPTION #language en-US "Library containing the business logic for the VariablePolicy engine" diff --git a/MdeModulePkg/Library/VariablePolicyLib/VariablePolicyUnitTest/VariablePolicyUnitTest.inf b/MdeModulePkg/Library/VariablePolicyLib/VariablePolicyUnitTest/VariablePolicyUnitTest.inf new file mode 100644 index 000000000000..c7c636eabde3 --- /dev/null +++ b/MdeModulePkg/Library/VariablePolicyLib/VariablePolicyUnitTest/VariablePolicyUnitTest.inf @@ -0,0 +1,41 @@ +## @file VariablePolicyUnitTest.inf +# UnitTest for... +# Business logic for Variable Policy enforcement. +# +## +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: BSD-2-Clause-Patent +## + + +[Defines] + INF_VERSION = 0x00010006 + BASE_NAME = VariablePolicyUnitTest + FILE_GUID = 1200A2E4-D756-418C-9768-528C2D181A98 + MODULE_TYPE = HOST_APPLICATION + VERSION_STRING = 1.0 + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = IA32 X64 ARM AARCH64 +# + +[Sources] + VariablePolicyUnitTest.c + + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + UnitTestFrameworkPkg/UnitTestFrameworkPkg.dec + + +[LibraryClasses] + BaseLib + DebugLib + UnitTestLib + PrintLib + VariablePolicyLib + BaseMemoryLib + MemoryAllocationLib diff --git a/MdeModulePkg/MdeModulePkg.dec b/MdeModulePkg/MdeModulePkg.dec index 956276e30a72..990e23b07a08 100644 --- a/MdeModulePkg/MdeModulePkg.dec +++ b/MdeModulePkg/MdeModulePkg.dec @@ -29,6 +29,9 @@ ## @libraryclass Defines a set of methods to reset whole system. ResetSystemLib|Include/Library/ResetSystemLib.h + ## @libraryclass Business logic for storing and testing variable policies + VariablePolicyLib|Include/Library/VariablePolicyLib.h + ## @libraryclass Defines a set of helper functions for resetting the system. ResetUtilityLib|Include/Library/ResetUtilityLib.h diff --git a/MdeModulePkg/MdeModulePkg.dsc b/MdeModulePkg/MdeModulePkg.dsc index f7dbb27ce25d..8501dae88eb1 100644 --- a/MdeModulePkg/MdeModulePkg.dsc +++ b/MdeModulePkg/MdeModulePkg.dsc @@ -3,6 +3,7 @@ # # (C) Copyright 2014 Hewlett-Packard Development Company, L.P.<BR> # Copyright (c) 2007 - 2019, Intel Corporation. All rights reserved.<BR> +# Copyright (c) Microsoft Corporation. # # SPDX-License-Identifier: BSD-2-Clause-Patent # @@ -58,6 +59,7 @@ DxeServicesLib|MdePkg/Library/DxeServicesLib/DxeServicesLib.inf DxeServicesTableLib|MdePkg/Library/DxeServicesTableLib/DxeServicesTableLib.inf UefiBootManagerLib|MdeModulePkg/Library/UefiBootManagerLib/UefiBootManagerLib.inf + VariablePolicyLib|MdeModulePkg/Library/VariablePolicyLib/VariablePolicyLib.inf # # Generic Modules # @@ -306,6 +308,7 @@ MdeModulePkg/Library/BootLogoLib/BootLogoLib.inf MdeModulePkg/Library/TpmMeasurementLibNull/TpmMeasurementLibNull.inf MdeModulePkg/Library/AuthVariableLibNull/AuthVariableLibNull.inf + MdeModulePkg/Library/VariablePolicyLib/VariablePolicyLib.inf MdeModulePkg/Library/VarCheckLib/VarCheckLib.inf MdeModulePkg/Library/VarCheckHiiLib/VarCheckHiiLib.inf MdeModulePkg/Library/VarCheckPcdLib/VarCheckPcdLib.inf diff --git a/MdeModulePkg/Test/MdeModulePkgHostTest.dsc b/MdeModulePkg/Test/MdeModulePkgHostTest.dsc index 72a119db4568..058ef7dcef11 100644 --- a/MdeModulePkg/Test/MdeModulePkgHostTest.dsc +++ b/MdeModulePkg/Test/MdeModulePkgHostTest.dsc @@ -19,12 +19,20 @@ !include UnitTestFrameworkPkg/UnitTestFrameworkPkgHost.dsc.inc +[LibraryClasses] + SafeIntLib|MdePkg/Library/BaseSafeIntLib/BaseSafeIntLib.inf + [Components] MdeModulePkg/Library/DxeResetSystemLib/UnitTest/MockUefiRuntimeServicesTableLib.inf # # Build MdeModulePkg HOST_APPLICATION Tests # + MdeModulePkg/Library/VariablePolicyLib/VariablePolicyUnitTest/VariablePolicyUnitTest.inf { + <LibraryClasses> + VariablePolicyLib|MdeModulePkg/Library/VariablePolicyLib/VariablePolicyLib.inf + } + MdeModulePkg/Library/DxeResetSystemLib/UnitTest/DxeResetSystemLibUnitTestHost.inf { <LibraryClasses> ResetSystemLib|MdeModulePkg/Library/DxeResetSystemLib/DxeResetSystemLib.inf -- 2.16.3.windows.1 ^ permalink raw reply related [flat|nested] 10+ messages in thread
* Re: [edk2-devel] [PATCH v1 2/9] MdeModulePkg: Define the VariablePolicyLib 2020-04-10 18:37 ` [PATCH v1 2/9] MdeModulePkg: Define the VariablePolicyLib Michael Kubacki @ 2020-04-22 9:14 ` Guomin Jiang 0 siblings, 0 replies; 10+ messages in thread From: Guomin Jiang @ 2020-04-22 9:14 UTC (permalink / raw) To: devel@edk2.groups.io, michael.kubacki@outlook.com Cc: Wang, Jian J, Wu, Hao A, Gao, Liming It is better that pay more attention to code style, for example Replace NewTable = AllocatePool( NewSize ); With space NewTable = AllocatePool (NewSize); Refer https://github.com/tianocore/tianocore.github.io/wiki/Code-Style-C for simple principle and https://edk2-docs.gitbooks.io/edk-ii-c-coding-standards-specification/content/ for detail principle. Below code can be optimized diff --git a/MdeModulePkg/Library/VariablePolicyLib/VariablePolicyLib.c b/MdeModulePkg/Library/VariablePolicyLib/VariablePolicyLib.c index 52e025f2d0..35bc70970b 100644 --- a/MdeModulePkg/Library/VariablePolicyLib/VariablePolicyLib.c +++ b/MdeModulePkg/Library/VariablePolicyLib/VariablePolicyLib.c @@ -337,7 +337,6 @@ RegisterVariablePolicy ( VARIABLE_POLICY_ENTRY *MatchPolicy; UINT8 MatchPriority; UINT32 NewSize; - UINT8 *NewTable; if (!IsVariablePolicyLibInitialized()) { return EFI_NOT_READY; @@ -376,16 +375,11 @@ RegisterVariablePolicy ( } // Reallocate and copy the table. - NewTable = AllocatePool( NewSize ); - if (NewTable == NULL) { + mPolicyTable = ReallocatePool (mCurrentTableUsage, NewSize, mPolicyTable); + if (mPolicyTable == NULL) { return EFI_OUT_OF_RESOURCES; } - CopyMem( NewTable, mPolicyTable, mCurrentTableUsage ); mCurrentTableSize = NewSize; - if (mPolicyTable != NULL) { - FreePool( mPolicyTable ); - } - mPolicyTable = NewTable; } // Copy the policy into the table. CopyMem( mPolicyTable + mCurrentTableUsage, NewPolicy, NewPolicy->Size ); > -----Original Message----- > From: devel@edk2.groups.io <devel@edk2.groups.io> On Behalf Of Michael > Kubacki > Sent: Saturday, April 11, 2020 2:38 AM > To: devel@edk2.groups.io > Cc: Wang, Jian J <jian.j.wang@intel.com>; Wu, Hao A <hao.a.wu@intel.com>; > Gao, Liming <liming.gao@intel.com> > Subject: [edk2-devel] [PATCH v1 2/9] MdeModulePkg: Define the > VariablePolicyLib > > From: Bret Barkelew <brbarkel@microsoft.com> > > https://bugzilla.tianocore.org/show_bug.cgi?id=2522 > > VariablePolicy is an updated interface to > replace VarLock and VarCheckProtocol. > > Add the VariablePolicyLib library that implements > the portable business logic for the VariablePolicy > engine. > > Also add host-based CI test cases for the lib. > > Cc: Jian J Wang <jian.j.wang@intel.com> > Cc: Hao A Wu <hao.a.wu@intel.com> > Cc: Liming Gao <liming.gao@intel.com> > Signed-off-by: Bret Barkelew <brbarkel@microsoft.com> > Signed-off-by: Michael Kubacki <michael.kubacki@microsoft.com> > --- > MdeModulePkg/Library/VariablePolicyLib/VariablePolicyLib.c > | 773 +++++++ > > MdeModulePkg/Library/VariablePolicyLib/VariablePolicyUnitTest/VariablePo > licyUnitTest.c | 2285 ++++++++++++++++++++ > MdeModulePkg/Include/Library/VariablePolicyLib.h | > 206 ++ > MdeModulePkg/Library/VariablePolicyLib/VariablePolicyLib.inf > | 38 + > MdeModulePkg/Library/VariablePolicyLib/VariablePolicyLib.uni > | 12 + > > MdeModulePkg/Library/VariablePolicyLib/VariablePolicyUnitTest/VariablePo > licyUnitTest.inf | 41 + > MdeModulePkg/MdeModulePkg.dec | 3 + > MdeModulePkg/MdeModulePkg.dsc | 3 + > MdeModulePkg/Test/MdeModulePkgHostTest.dsc | > 8 + > 9 files changed, 3369 insertions(+) > > diff --git a/MdeModulePkg/Library/VariablePolicyLib/VariablePolicyLib.c > b/MdeModulePkg/Library/VariablePolicyLib/VariablePolicyLib.c > new file mode 100644 > index 000000000000..52e025f2d0cf > --- /dev/null > +++ b/MdeModulePkg/Library/VariablePolicyLib/VariablePolicyLib.c > @@ -0,0 +1,773 @@ > +/** @file -- VariablePolicyLib.c > +Business logic for Variable Policy enforcement. > + > +Copyright (c) Microsoft Corporation. > +SPDX-License-Identifier: BSD-2-Clause-Patent > + > +**/ > + > +#include <Uefi.h> > + > +#include <Library/SafeIntLib.h> > +#include <Library/MemoryAllocationLib.h> > +#include <Library/BaseMemoryLib.h> > +#include <Protocol/VariablePolicy.h> > +#include <Library/VariablePolicyLib.h> > + > +#include <Library/DebugLib.h> > + > +// IMPORTANT NOTE: This library is currently rife with multiple return > statements > +// for error handling. A refactor should remove these at some point. > + > +// > +// This library was designed with advanced unit-test features. > +// This define handles the configuration. > +#ifdef INTERNAL_UNIT_TEST > +#undef STATIC > +#define STATIC // Nothing... > +#endif > + > +// An abstracted GetVariable interface that enables configuration > regardless of the environment. > +STATIC EFI_GET_VARIABLE mGetVariableHelper = NULL; > + > +// Master switch to lock this entire interface. Does not stop enforcement, > +// just prevents the configuration from being changed for the rest of the > boot. > +STATIC BOOLEAN mInterfaceLocked = FALSE; > + > +// Master switch to disable the entire interface for a single boot. > +// This will disable all policy enforcement for the duration of the boot. > +STATIC BOOLEAN mProtectionDisabled = FALSE; > + > +// Table to hold all the current policies. > +STATIC UINT8 *mPolicyTable = NULL; > +STATIC UINT32 mCurrentTableSize = 0; > +STATIC UINT32 mCurrentTableUsage = 0; > +STATIC UINT32 mCurrentTableCount = 0; > + > +#define POLICY_TABLE_STEP_SIZE 0x1000 > + > +// NOTE: DO NOT USE THESE MACROS on any structure that has not been > validated. > +// Current table data has already been sanitized. > +#define GET_NEXT_POLICY(CurPolicy) > (VARIABLE_POLICY_ENTRY*)((UINT8*)CurPolicy + CurPolicy->Size) > +#define GET_POLICY_NAME(CurPolicy) (CHAR16*)((UINTN)CurPolicy + > CurPolicy->OffsetToName) > + > +#define MATCH_PRIORITY_EXACT 0 > +#define MATCH_PRIORITY_MAX MATCH_PRIORITY_EXACT > +#define MATCH_PRIORITY_MIN MAX_UINT8 > + > + > +/** > + This helper function determines whether the structure of an incoming > policy > + is valid and internally consistent. > + > + @param[in] NewPolicy Pointer to the incoming policy structure. > + > + @retval TRUE > + @retval FALSE Pointer is NULL, size is wrong, strings are empty, or > + substructures overlap. > + > +**/ > +STATIC > +BOOLEAN > +IsValidVariablePolicyStructure ( > + IN CONST VARIABLE_POLICY_ENTRY *NewPolicy > + ) > +{ > + EFI_STATUS Status; > + UINTN EntryEnd; > + CHAR16 *CheckChar; > + UINTN WildcardCount; > + > + // Sanitize some quick values. > + if (NewPolicy == NULL || NewPolicy->Size == 0 || > + // Structure size should be at least as long as the minumum structure and > a NULL string. > + NewPolicy->Size < sizeof(VARIABLE_POLICY_ENTRY) || > + // Check for the known revision. > + NewPolicy->Version != VARIABLE_POLICY_ENTRY_REVISION) { > + return FALSE; > + } > + > + // Calculate the theoretical end of the structure and make sure > + // that the structure can fit in memory. > + Status = SafeUintnAdd( (UINTN)NewPolicy, NewPolicy->Size, &EntryEnd ); > + if (EFI_ERROR( Status )) { > + return FALSE; > + } > + > + // Check for a valid Max Size. > + if (NewPolicy->MaxSize == 0) { > + return FALSE; > + } > + > + // Check for the valid list of lock policies. > + if (NewPolicy->LockPolicyType != VARIABLE_POLICY_TYPE_NO_LOCK && > + NewPolicy->LockPolicyType != VARIABLE_POLICY_TYPE_LOCK_NOW && > + NewPolicy->LockPolicyType != > VARIABLE_POLICY_TYPE_LOCK_ON_CREATE && > + NewPolicy->LockPolicyType != > VARIABLE_POLICY_TYPE_LOCK_ON_VAR_STATE) > + { > + return FALSE; > + } > + > + // If the policy type is VARIABLE_POLICY_TYPE_LOCK_ON_VAR_STATE, > make sure that the matching state variable Name > + // terminates before the OffsetToName for the matching policy variable > Name. > + if (NewPolicy->LockPolicyType == > VARIABLE_POLICY_TYPE_LOCK_ON_VAR_STATE) { > + // Adjust CheckChar to the offset of the LockPolicy->Name. > + Status = SafeUintnAdd( (UINTN)NewPolicy + > sizeof(VARIABLE_POLICY_ENTRY), > + sizeof(VARIABLE_LOCK_ON_VAR_STATE_POLICY), > + (UINTN*)&CheckChar ); > + if (EFI_ERROR( Status ) || EntryEnd <= (UINTN)CheckChar) { > + return FALSE; > + } > + while (*CheckChar != CHAR_NULL) { > + if (EntryEnd <= (UINTN)CheckChar) { > + return FALSE; > + } > + CheckChar++; > + } > + // At this point we should have either exeeded the structure or be > pointing at the last char in LockPolicy->Name. > + // We should check to make sure that the policy Name comes > immediately after this charcter. > + if ((UINTN)++CheckChar != (UINTN)NewPolicy + NewPolicy- > >OffsetToName) { > + return FALSE; > + } > + } > + // If the policy type is any other value, make sure that the LockPolicy > structure has a zero length. > + else { > + if (NewPolicy->OffsetToName != sizeof(VARIABLE_POLICY_ENTRY)) { > + return FALSE; > + } > + } > + > + // Check to make sure that the name has a terminating character > + // before the end of the structure. > + // We've already checked that the name is within the bounds of the > structure. > + if (NewPolicy->Size != NewPolicy->OffsetToName) { > + CheckChar = (CHAR16*)((UINTN)NewPolicy + NewPolicy- > >OffsetToName); > + WildcardCount = 0; > + while (*CheckChar != CHAR_NULL) { > + // Make sure there aren't excessive wildcards. > + if (*CheckChar == '#') { > + WildcardCount++; > + if (WildcardCount > MATCH_PRIORITY_MIN) { > + return FALSE; > + } > + } > + // Make sure you're still within the bounds of the policy structure. > + if (EntryEnd <= (UINTN)CheckChar) { > + return FALSE; > + } > + CheckChar++; > + } > + > + // Finally, we should be pointed at the very last character in Name, so we > should be right > + // up against the end of the structure. > + if ((UINTN)++CheckChar != EntryEnd) { > + return FALSE; > + } > + } > + > + return TRUE; > +} > + > + > +/** > + This helper function evaluates a policy and determines whether it matches > the target > + variable. If matched, will also return a value corresponding to the priority > of the match. > + > + The rules for "best match" are listed in the Variable Policy Spec. > + Perfect name matches will return 0. > + Single wildcard characters will return the number of wildcard characters. > + Full namespaces will return MAX_UINT8. > + > + @param[in] EvalEntry Pointer to the policy entry being evaluated. > + @param[in] VariableName Same as EFI_SET_VARIABLE. > + @param[in] VendorGuid Same as EFI_SET_VARIABLE. > + @param[out] MatchPriority [Optional] On finding a match, this value > contains the priority of the match. > + Lower number == higher priority. Only valid if a match found. > + > + @retval TRUE Current entry matches the target variable. > + @retval FALSE Current entry does not match at all. > + > +**/ > +STATIC > +BOOLEAN > +EvaluatePolicyMatch ( > + IN CONST VARIABLE_POLICY_ENTRY *EvalEntry, > + IN CONST CHAR16 *VariableName, > + IN CONST EFI_GUID *VendorGuid, > + OUT UINT8 *MatchPriority OPTIONAL > + ) > +{ > + BOOLEAN Result = FALSE; > + CHAR16 *PolicyName; > + UINT8 CalculatedPriority = MATCH_PRIORITY_EXACT; > + UINTN Index; > + > + // Step 1: If the GUID doesn't match, we're done. No need to evaluate > anything else. > + if (!CompareGuid( &EvalEntry->Namespace, VendorGuid )) { > + goto Exit; > + } > + > + // If the GUID matches, check to see whether there is a Name associated > + // with the policy. If not, this policy matches the entire namespace. > + // Missing Name is indicated by size being equal to name. > + if (EvalEntry->Size == EvalEntry->OffsetToName) { > + CalculatedPriority = MATCH_PRIORITY_MIN; > + Result = TRUE; > + goto Exit; > + } > + > + // Now that we know the name exists, get it. > + PolicyName = GET_POLICY_NAME( EvalEntry ); > + > + // Evaluate the name against the policy name and check for a match. > + // Account for any wildcards. > + Index = 0; > + Result = TRUE; > + // Keep going until the end of both strings. > + while (PolicyName[Index] != CHAR_NULL || VariableName[Index] != > CHAR_NULL) { > + // If we don't have a match... > + if (PolicyName[Index] != VariableName[Index] || PolicyName[Index] == > '#') { > + // If this is a numerical wildcard, we can consider > + // it a match if we alter the priority. > + if (PolicyName[Index] == L'#' && > + (L'0' <= VariableName[Index] && VariableName[Index] <= L'9')) { > + if (CalculatedPriority < MATCH_PRIORITY_MIN) { > + CalculatedPriority++; > + } > + } > + // Otherwise, not a match. > + else { > + Result = FALSE; > + goto Exit; > + } > + } > + Index++; > + } > + > +Exit: > + if (Result && MatchPriority != NULL) { > + *MatchPriority = CalculatedPriority; > + } > + return Result; > +} > + > + > +/** > + This helper function walks the current policy table and returns a pointer > + to the best match, if any are found. Leverages EvaluatePolicyMatch() to > + determine "best". > + > + @param[in] VariableName Same as EFI_SET_VARIABLE. > + @param[in] VendorGuid Same as EFI_SET_VARIABLE. > + @param[out] ReturnPriority [Optional] If pointer is provided, return the > + priority of the match. Same as EvaluatePolicyMatch(). > + Only valid if a match is returned. > + > + @retval VARIABLE_POLICY_ENTRY* Best match that was found. > + @retval NULL No match was found. > + > +**/ > +STATIC > +VARIABLE_POLICY_ENTRY* > +GetBestPolicyMatch ( > + IN CONST CHAR16 *VariableName, > + IN CONST EFI_GUID *VendorGuid, > + OUT UINT8 *ReturnPriority OPTIONAL > + ) > +{ > + VARIABLE_POLICY_ENTRY *BestResult = NULL; > + VARIABLE_POLICY_ENTRY *CurrentEntry; > + UINT8 MatchPriority; > + UINT8 CurrentPriority; > + UINTN Index; > + > + // Walk all entries in the table, looking for matches. > + CurrentEntry = (VARIABLE_POLICY_ENTRY*)mPolicyTable; > + for (Index = 0; Index < mCurrentTableCount; Index++) { > + // Check for a match. > + if (EvaluatePolicyMatch( CurrentEntry, VariableName, VendorGuid, > &CurrentPriority ) == TRUE) { > + // If match is better, take it. > + if (BestResult == NULL || CurrentPriority < MatchPriority) { > + BestResult = CurrentEntry; > + MatchPriority = CurrentPriority; > + } > + > + // If you've hit the highest-priority match, can exit now. > + if (MatchPriority == 0) { > + break; > + } > + } > + > + // If we're still in the loop, move to the next entry. > + CurrentEntry = GET_NEXT_POLICY( CurrentEntry ); > + } > + > + // If a return priority was requested, return it. > + if (ReturnPriority != NULL) { > + *ReturnPriority = MatchPriority; > + } > + > + return BestResult; > +} > + > + > +/** > + This API function validates and registers a new policy with > + the policy enforcement engine. > + > + @param[in] NewPolicy Pointer to the incoming policy structure. > + > + @retval EFI_SUCCESS > + @retval EFI_INVALID_PARAMETER NewPolicy is NULL or is internally > inconsistent. > + @retval EFI_ALREADY_STARTED An identical matching policy already > exists. > + @retval EFI_WRITE_PROTECTED The interface has been locked until > the next reboot. > + @retval EFI_UNSUPPORTED Policy enforcement has been disabled. > No reason to add more policies. > + @retval EFI_ABORTED A calculation error has prevented this > function from completing. > + @retval EFI_OUT_OF_RESOURCES Cannot grow the table to hold any > more policies. > + @retval EFI_NOT_READY Library has not yet been initialized. > + > +**/ > +EFI_STATUS > +EFIAPI > +RegisterVariablePolicy ( > + IN CONST VARIABLE_POLICY_ENTRY *NewPolicy > + ) > +{ > + EFI_STATUS Status; > + VARIABLE_POLICY_ENTRY *MatchPolicy; > + UINT8 MatchPriority; > + UINT32 NewSize; > + UINT8 *NewTable; > + > + if (!IsVariablePolicyLibInitialized()) { > + return EFI_NOT_READY; > + } > + if (mInterfaceLocked) { > + return EFI_WRITE_PROTECTED; > + } > + > + if (!IsValidVariablePolicyStructure( NewPolicy )) { > + return EFI_INVALID_PARAMETER; > + } > + > + // Check to see whether an exact matching policy already exists. > + MatchPolicy = GetBestPolicyMatch( GET_POLICY_NAME( NewPolicy ), > + &NewPolicy->Namespace, > + &MatchPriority ); > + if (MatchPolicy != NULL && MatchPriority == MATCH_PRIORITY_EXACT) { > + return EFI_ALREADY_STARTED; > + } > + > + // If none exists, create it. > + // If we need more space, allocate that now. > + Status = SafeUint32Add( mCurrentTableUsage, NewPolicy->Size, > &NewSize ); > + if (EFI_ERROR( Status )) { > + return EFI_ABORTED; > + } > + if (NewSize > mCurrentTableSize) { > + // Use NewSize to calculate the new table size in units of > POLICY_TABLE_STEP_SIZE. > + NewSize = (NewSize % POLICY_TABLE_STEP_SIZE) > 0 ? > + (NewSize / POLICY_TABLE_STEP_SIZE) + 1 : > + (NewSize / POLICY_TABLE_STEP_SIZE); > + // Calculate the new table size in absolute bytes. > + Status = SafeUint32Mult( NewSize, POLICY_TABLE_STEP_SIZE, > &NewSize ); > + if (EFI_ERROR( Status )) { > + return EFI_ABORTED; > + } > + > + // Reallocate and copy the table. > + NewTable = AllocatePool( NewSize ); > + if (NewTable == NULL) { > + return EFI_OUT_OF_RESOURCES; > + } > + CopyMem( NewTable, mPolicyTable, mCurrentTableUsage ); > + mCurrentTableSize = NewSize; > + if (mPolicyTable != NULL) { > + FreePool( mPolicyTable ); > + } > + mPolicyTable = NewTable; > + } > + // Copy the policy into the table. > + CopyMem( mPolicyTable + mCurrentTableUsage, NewPolicy, NewPolicy- > >Size ); > + mCurrentTableUsage += NewPolicy->Size; > + mCurrentTableCount += 1; > + > + // We're done here. > + > + return EFI_SUCCESS; > +} > + > + > +/** > + This API function checks to see whether the parameters to SetVariable > would > + be allowed according to the current variable policies. > + > + @param[in] VariableName Same as EFI_SET_VARIABLE. > + @param[in] VendorGuid Same as EFI_SET_VARIABLE. > + @param[in] Attributes Same as EFI_SET_VARIABLE. > + @param[in] DataSize Same as EFI_SET_VARIABLE. > + @param[in] Data Same as EFI_SET_VARIABLE. > + > + @retval EFI_SUCCESS A matching policy allows this update. > + @retval EFI_SUCCESS There are currently no policies that restrict > this update. > + @retval EFI_SUCCESS The protections have been disable until the > next reboot. > + @retval EFI_WRITE_PROTECTED Variable is currently locked. > + @retval EFI_INVALID_PARAMETER Attributes or size are invalid. > + @retval EFI_ABORTED A lock policy exists, but an error prevented > evaluation. > + @retval EFI_NOT_READY Library has not been initialized. > + > +**/ > +EFI_STATUS > +EFIAPI > +ValidateSetVariable ( > + IN CHAR16 *VariableName, > + IN EFI_GUID *VendorGuid, > + IN UINT32 Attributes, > + IN UINTN DataSize, > + IN VOID *Data > + ) > +{ > + BOOLEAN IsDel; > + VARIABLE_POLICY_ENTRY *ActivePolicy; > + EFI_STATUS Status; > + EFI_STATUS ReturnStatus = EFI_SUCCESS; > + VARIABLE_LOCK_ON_VAR_STATE_POLICY *StateVarPolicy; > + CHAR16 *StateVarName; > + UINTN StateVarSize; > + UINT8 StateVar; > + > + if (!IsVariablePolicyLibInitialized()) { > + ReturnStatus = EFI_NOT_READY; > + goto Exit; > + } > + > + // Bail if the protections are currently disabled. > + if (mProtectionDisabled == TRUE) { > + ReturnStatus = EFI_SUCCESS; > + goto Exit; > + } > + > + // Determine whether this is a delete operation. > + // If so, it will affect which tests are applied. > + if ((DataSize == 0) && ((Attributes & EFI_VARIABLE_APPEND_WRITE) == 0)) > { > + IsDel = TRUE; > + } > + else { > + IsDel = FALSE; > + } > + > + // Find an active policy if one exists. > + ActivePolicy = GetBestPolicyMatch( VariableName, VendorGuid, NULL ); > + > + // If we have an active policy, check it against the incoming data. > + if (ActivePolicy != NULL) { > + // > + // Only enforce size and attribute constraints when updating data, not > deleting. > + if (!IsDel) { > + // Check for size constraints. > + if ((ActivePolicy->MinSize > 0 && DataSize < ActivePolicy->MinSize) || > + (ActivePolicy->MaxSize > 0 && DataSize > ActivePolicy->MaxSize)) { > + ReturnStatus = EFI_INVALID_PARAMETER; > + DEBUG(( DEBUG_VERBOSE, "%a - Bad Size. 0x%X <> 0x%X-0x%X\n", > __FUNCTION__, > + DataSize, ActivePolicy->MinSize, ActivePolicy->MaxSize )); > + goto Exit; > + } > + > + // Check for attribute constraints. > + if ((ActivePolicy->AttributesMustHave & Attributes) != ActivePolicy- > >AttributesMustHave || > + (ActivePolicy->AttributesCantHave & Attributes) != 0) { > + ReturnStatus = EFI_INVALID_PARAMETER; > + DEBUG(( DEBUG_VERBOSE, "%a - Bad Attributes. 0x%X <> > 0x%X:0x%X\n", __FUNCTION__, > + Attributes, ActivePolicy->AttributesMustHave, ActivePolicy- > >AttributesCantHave )); > + goto Exit; > + } > + } > + > + // > + // Lock policy check. > + // > + // Check for immediate lock. > + if (ActivePolicy->LockPolicyType == VARIABLE_POLICY_TYPE_LOCK_NOW) > { > + ReturnStatus = EFI_WRITE_PROTECTED; > + goto Exit; > + } > + // Check for lock on create. > + else if (ActivePolicy->LockPolicyType == > VARIABLE_POLICY_TYPE_LOCK_ON_CREATE) { > + StateVarSize = 0; > + Status = mGetVariableHelper( VariableName, > + VendorGuid, > + NULL, > + &StateVarSize, > + NULL ); > + if (Status == EFI_BUFFER_TOO_SMALL) { > + ReturnStatus = EFI_WRITE_PROTECTED; > + goto Exit; > + } > + } > + // Check for lock on state variable. > + else if (ActivePolicy->LockPolicyType == > VARIABLE_POLICY_TYPE_LOCK_ON_VAR_STATE) { > + StateVarPolicy = > (VARIABLE_LOCK_ON_VAR_STATE_POLICY*)((UINT8*)ActivePolicy + > sizeof(VARIABLE_POLICY_ENTRY)); > + StateVarName = (CHAR16*)((UINT8*)StateVarPolicy + > sizeof(VARIABLE_LOCK_ON_VAR_STATE_POLICY)); > + StateVarSize = sizeof(StateVar); > + Status = mGetVariableHelper( StateVarName, > + &StateVarPolicy->Namespace, > + NULL, > + &StateVarSize, > + &StateVar ); > + > + // If the variable was found, check the state. If matched, this variable is > locked. > + if (!EFI_ERROR( Status )) { > + if (StateVar == StateVarPolicy->Value) { > + ReturnStatus = EFI_WRITE_PROTECTED; > + goto Exit; > + } > + } > + // EFI_NOT_FOUND and EFI_BUFFER_TOO_SMALL indicate that the > state doesn't match. > + else if (Status != EFI_NOT_FOUND && Status != > EFI_BUFFER_TOO_SMALL) { > + // We don't know what happened, but it isn't good. > + ReturnStatus = EFI_ABORTED; > + goto Exit; > + } > + } > + } > + > +Exit: > + DEBUG(( DEBUG_VERBOSE, "%a - Variable (%g:%s) returning %r.\n", > __FUNCTION__, VendorGuid, VariableName, ReturnStatus )); > + return ReturnStatus; > +} > + > + > +/** > + This API function disables the variable policy enforcement. If it's > + already been called once, will return EFI_ALREADY_STARTED. > + > + @retval EFI_SUCCESS > + @retval EFI_ALREADY_STARTED Has already been called once this boot. > + @retval EFI_WRITE_PROTECTED Interface has been locked until reboot. > + @retval EFI_NOT_READY Library has not yet been initialized. > + > +**/ > +EFI_STATUS > +EFIAPI > +DisableVariablePolicy ( > + VOID > + ) > +{ > + if (!IsVariablePolicyLibInitialized()) { > + return EFI_NOT_READY; > + } > + if (mProtectionDisabled) { > + return EFI_ALREADY_STARTED; > + } > + if (mInterfaceLocked) { > + return EFI_WRITE_PROTECTED; > + } > + mProtectionDisabled = TRUE; > + return EFI_SUCCESS; > +} > + > + > +/** > + This API function will dump the entire contents of the variable policy table. > + > + Similar to GetVariable, the first call can be made with a 0 size and it will > return > + the size of the buffer required to hold the entire table. > + > + @param[out] Policy Pointer to the policy buffer. Can be NULL if Size is 0. > + @param[in,out] Size On input, the size of the output buffer. On output, > the size > + of the data returned. > + > + @retval EFI_SUCCESS Policy data is in the output buffer and Size > has been updated. > + @retval EFI_INVALID_PARAMETER Size is NULL, or Size is non-zero and > Policy is NULL. > + @retval EFI_BUFFER_TOO_SMALL Size is insufficient to hold policy. Size > updated with required size. > + @retval EFI_NOT_READY Library has not yet been initialized. > + > +**/ > +EFI_STATUS > +EFIAPI > +DumpVariablePolicy ( > + OUT UINT8 *Policy, > + IN OUT UINT32 *Size > + ) > +{ > + if (!IsVariablePolicyLibInitialized()) { > + return EFI_NOT_READY; > + } > + > + // Check the parameters. > + if (Size == NULL || (*Size > 0 && Policy == NULL)) { > + return EFI_INVALID_PARAMETER; > + } > + > + // Make sure the size is sufficient to hold the policy table. > + if (*Size < mCurrentTableUsage) { > + *Size = mCurrentTableUsage; > + return EFI_BUFFER_TOO_SMALL; > + } > + > + // If we're still here, copy the table and bounce. > + CopyMem( Policy, mPolicyTable, mCurrentTableUsage ); > + *Size = mCurrentTableUsage; > + > + return EFI_SUCCESS; > +} > + > + > +/** > + This API function returns whether or not the policy engine is > + currently being enforced. > + > + @retval TRUE > + @retval FALSE > + @retval FALSE Library has not yet been initialized. > + > +**/ > +BOOLEAN > +EFIAPI > +IsVariablePolicyEnabled ( > + VOID > + ) > +{ > + if (!IsVariablePolicyLibInitialized()) { > + return FALSE; > + } > + return !mProtectionDisabled; > +} > + > + > +/** > + This API function locks the interface so that no more policy updates > + can be performed or changes made to the enforcement until the next > boot. > + > + @retval EFI_SUCCESS > + @retval EFI_NOT_READY Library has not yet been initialized. > + > +**/ > +EFI_STATUS > +EFIAPI > +LockVariablePolicy ( > + VOID > + ) > +{ > + if (!IsVariablePolicyLibInitialized()) { > + return EFI_NOT_READY; > + } > + if (mInterfaceLocked) { > + return EFI_WRITE_PROTECTED; > + } > + mInterfaceLocked = TRUE; > + return EFI_SUCCESS; > +} > + > + > +/** > + This API function returns whether or not the policy interface is locked > + for the remainder of the boot. > + > + @retval TRUE > + @retval FALSE > + @retval FALSE Library has not yet been initialized. > + > +**/ > +BOOLEAN > +EFIAPI > +IsVariablePolicyInterfaceLocked ( > + VOID > + ) > +{ > + if (!IsVariablePolicyLibInitialized()) { > + return FALSE; > + } > + return mInterfaceLocked; > +} > + > + > +/** > + This helper function initializes the library and sets > + up any required internal structures or handlers. > + > + Also registers the internal pointer for the GetVariable helper. > + > + @param[in] GetVariableHelper A function pointer matching the > EFI_GET_VARIABLE prototype that will be used to > + check policy criteria that involve the existence of other variables. > + > + @retval EFI_SUCCESS > + @retval EFI_ALREADY_STARTED The initialize function has been called > more than once without a call to > + deinitialize. > + > +**/ > +EFI_STATUS > +EFIAPI > +InitVariablePolicyLib ( > + IN EFI_GET_VARIABLE GetVariableHelper > + ) > +{ > + if (mGetVariableHelper != NULL) { > + return EFI_ALREADY_STARTED; > + } > + > + // Save an internal pointer to the GetVariableHelper. > + mGetVariableHelper = GetVariableHelper; > + > + // Initialize the global state. > + mInterfaceLocked = FALSE; > + mProtectionDisabled = FALSE; > + mPolicyTable = NULL; > + mCurrentTableSize = 0; > + mCurrentTableUsage = 0; > + mCurrentTableCount = 0; > + > + return EFI_SUCCESS; > +} > + > + > +/** > + This helper function returns whether or not the library is currently > initialized. > + > + @retval TRUE > + @retval FALSE > + > +**/ > +BOOLEAN > +EFIAPI > +IsVariablePolicyLibInitialized ( > + VOID > + ) > +{ > + return (mGetVariableHelper != NULL); > +} > + > + > +/** > + This helper function tears down the library. > + > + Should generally only be used for test harnesses. > + > + @retval EFI_SUCCESS > + @retval EFI_NOT_READY Deinitialize was called without first calling > initialize. > + > +**/ > +EFI_STATUS > +EFIAPI > +DeinitVariablePolicyLib ( > + VOID > + ) > +{ > + if (mGetVariableHelper == NULL) { > + return EFI_NOT_READY; > + } > + > + mGetVariableHelper = NULL; > + mInterfaceLocked = FALSE; > + mProtectionDisabled = FALSE; > + mCurrentTableSize = 0; > + mCurrentTableUsage = 0; > + mCurrentTableCount = 0; > + > + if (mPolicyTable != NULL) { > + FreePool( mPolicyTable ); > + mPolicyTable = NULL; > + } > + > + return EFI_SUCCESS; > +} > diff --git > a/MdeModulePkg/Library/VariablePolicyLib/VariablePolicyUnitTest/Variable > PolicyUnitTest.c > b/MdeModulePkg/Library/VariablePolicyLib/VariablePolicyUnitTest/Variable > PolicyUnitTest.c > new file mode 100644 > index 000000000000..3214bff09091 > --- /dev/null > +++ > b/MdeModulePkg/Library/VariablePolicyLib/VariablePolicyUnitTest/Variable > PolicyUnitTest.c > @@ -0,0 +1,2285 @@ > +/** @file -- VariablePolicyUnitTest.c > +UnitTest for... > +Business logic for Variable Policy enforcement. > + > +Copyright (c) Microsoft Corporation. > +SPDX-License-Identifier: BSD-2-Clause-Patent > + > +**/ > + > +#include <stdio.h> > +#include <string.h> > +#include <stdarg.h> > +#include <stddef.h> > +#include <setjmp.h> > +#include <cmocka.h> > + > +#include <Uefi.h> > +#include <Library/PrintLib.h> > +#include <Library/DebugLib.h> > +#include <Library/UnitTestLib.h> > +#include <Library/BaseMemoryLib.h> > +#include <Library/MemoryAllocationLib.h> > +#include <Library/BaseLib.h> > + > +#include <Guid/VariableFormat.h> > + > +#include <Protocol/VariablePolicy.h> > +#include <Library/VariablePolicyLib.h> > + > +// MU_CHANGE - Turn this off for now. Try to turn it back on with extra > build options. > +// #ifndef INTERNAL_UNIT_TEST > +// #error Make sure to build thie with INTERNAL_UNIT_TEST enabled! > Otherwise, some important tests may be skipped! > +// #endif > + > + > +#define UNIT_TEST_NAME "UEFI Variable Policy UnitTest" > +#define UNIT_TEST_VERSION "0.5" > + > +///=== TEST DATA > ========================================================== > ======================== > + > +#pragma pack(push, 1) > +typedef struct _SIMPLE_VARIABLE_POLICY_ENTRY { > + VARIABLE_POLICY_ENTRY Header; > + CHAR16 Name[]; > +} SIMPLE_VARIABLE_POLICY_ENTRY; > +#define EXPANDED_VARIABLE_POLICY_ENTRY_VAR_NAME_LENGTH 1001 > // 1000 characters + terminator. > +#define EXPANDED_VARIABLE_POLICY_ENTRY_VAR_NAME_SIZE > (EXPANDED_VARIABLE_POLICY_ENTRY_VAR_NAME_LENGTH * > sizeof(CHAR16)) > +typedef struct _EXPANDED_VARIABLE_POLICY_ENTRY { > + VARIABLE_POLICY_ENTRY Header; > + VARIABLE_LOCK_ON_VAR_STATE_POLICY StatePolicy; > + CHAR16 > StateName[EXPANDED_VARIABLE_POLICY_ENTRY_VAR_NAME_LENGTH]; > + CHAR16 > Name[EXPANDED_VARIABLE_POLICY_ENTRY_VAR_NAME_LENGTH]; > +} EXPANDED_VARIABLE_POLICY_ENTRY; > +#pragma pack(pop) > + > +// {F955BA2D-4A2C-480C-BFD1-3CC522610592} > +#define TEST_GUID_1 { 0xf955ba2d, 0x4a2c, 0x480c, { 0xbf, 0xd1, 0x3c, 0xc5, > 0x22, 0x61, 0x5, 0x92 } } > +EFI_GUID mTestGuid1 = TEST_GUID_1; > +// {2DEA799E-5E73-43B9-870E-C945CE82AF3A} > +#define TEST_GUID_2 { 0x2dea799e, 0x5e73, 0x43b9, { 0x87, 0xe, 0xc9, 0x45, > 0xce, 0x82, 0xaf, 0x3a } } > +EFI_GUID mTestGuid2 = TEST_GUID_2; > +// {698A2BFD-A616-482D-B88C-7100BD6682A9} > +#define TEST_GUID_3 { 0x698a2bfd, 0xa616, 0x482d, { 0xb8, 0x8c, 0x71, 0x0, > 0xbd, 0x66, 0x82, 0xa9 } } > +EFI_GUID mTestGuid3 = TEST_GUID_3; > + > +#define TEST_VAR_1_NAME L"TestVar1" > +#define TEST_VAR_2_NAME L"TestVar2" > +#define TEST_VAR_3_NAME L"TestVar3" > + > +#define TEST_POLICY_ATTRIBUTES_NULL 0 > +#define TEST_POLICY_MIN_SIZE_NULL 0 > +#define TEST_POLICY_MAX_SIZE_NULL MAX_UINT32 > + > +#define TEST_POLICY_MIN_SIZE_10 10 > +#define TEST_POLICY_MAX_SIZE_200 200 > + > +#define TEST_300_HASHES_STRING > L"##################################################"\ > + > "##################################################"\ > + > "##################################################"\ > + > "##################################################"\ > + > "##################################################"\ > + > "##################################################" > + > + > +///=== HELPER FUNCTIONS > ========================================================== > ================= > + > +STATIC > +BOOLEAN > +InitExpVarPolicyStrings ( > + EXPANDED_VARIABLE_POLICY_ENTRY *Entry, > + CHAR16 *Name, OPTIONAL > + CHAR16 *StateName OPTIONAL > + ) > +{ > + UINTN NameSize; > + UINTN StateNameSize; > + > + NameSize = Name == NULL ? 0 : StrSize( Name ); > + StateNameSize = StateName == NULL ? 0 : StrSize( StateName ); > + > + if (NameSize > EXPANDED_VARIABLE_POLICY_ENTRY_VAR_NAME_SIZE || > NameSize > MAX_UINT16 || > + StateNameSize > > EXPANDED_VARIABLE_POLICY_ENTRY_VAR_NAME_SIZE || StateNameSize > > MAX_UINT16) { > + return FALSE; > + } > + > + Entry->Header.OffsetToName = sizeof(VARIABLE_POLICY_ENTRY); > + if (StateName != NULL) { > + Entry->Header.OffsetToName += > (UINT16)sizeof(VARIABLE_LOCK_ON_VAR_STATE_POLICY) + > (UINT16)StateNameSize; > + } > + Entry->Header.Size = Entry->Header.OffsetToName + (UINT16)NameSize; > + > + CopyMem( (UINT8*)Entry + Entry->Header.OffsetToName, Name, > NameSize ); > + if (StateName != NULL) { > + CopyMem( (UINT8*)Entry + sizeof(VARIABLE_POLICY_ENTRY) + > sizeof(VARIABLE_LOCK_ON_VAR_STATE_POLICY), StateName, > StateNameSize ); > + } > + > + return TRUE; > +} > + > +EFI_STATUS > +EFIAPI > +StubGetVariableNull ( > + IN CHAR16 *VariableName, > + IN EFI_GUID *VendorGuid, > + OUT UINT32 *Attributes, OPTIONAL > + IN OUT UINTN *DataSize, > + OUT VOID *Data OPTIONAL > + ) > +{ > + UINT32 MockedAttr; > + UINTN MockedDataSize; > + VOID *MockedData; > + EFI_STATUS MockedReturn; > + > + check_expected_ptr( VariableName ); > + check_expected_ptr( VendorGuid ); > + check_expected_ptr( DataSize ); > + > + MockedAttr = (UINT32)mock(); > + MockedDataSize = (UINTN)mock(); > + MockedData = (VOID*)mock(); > + MockedReturn = (EFI_STATUS)mock(); > + > + if (Attributes) { > + *Attributes = MockedAttr; > + } > + if (Data && !EFI_ERROR(MockedReturn)) { > + CopyMem( Data, MockedData, MockedDataSize ); > + } > + > + *DataSize = MockedDataSize; > + > + return MockedReturn; > +} > + > +// > +// Anything you think might be helpful that isn't a test itself. > +// > + > +STATIC > +UNIT_TEST_STATUS > +LibInitMocked ( > + IN UNIT_TEST_CONTEXT Context > + ) > +{ > + return EFI_ERROR(InitVariablePolicyLib( StubGetVariableNull )) ? > UNIT_TEST_ERROR_PREREQUISITE_NOT_MET : UNIT_TEST_PASSED; > +} > + > +STATIC > +VOID > +LibCleanup ( > + IN UNIT_TEST_CONTEXT Context > + ) > +{ > + DeinitVariablePolicyLib(); > +} > + > + > +///=== TEST CASES > ========================================================== > ======================= > + > +///===== ARCHITECTURAL SUITE > ================================================== > + > +UNIT_TEST_STATUS > +EFIAPI > +ShouldBeAbleToInitAndDeinitTheLibrary ( > + IN UNIT_TEST_CONTEXT Context > + ) > +{ > + EFI_STATUS Status; > + Status = InitVariablePolicyLib( StubGetVariableNull ); > + UT_ASSERT_NOT_EFI_ERROR( Status ); > + > + UT_ASSERT_TRUE( IsVariablePolicyLibInitialized() ); > + > + Status = DeinitVariablePolicyLib(); > + UT_ASSERT_NOT_EFI_ERROR( Status ); > + > + UT_ASSERT_FALSE( IsVariablePolicyLibInitialized() ); > + > + return UNIT_TEST_PASSED; > +} > + > +UNIT_TEST_STATUS > +EFIAPI > +ShouldNotBeAbleToInitializeTheLibraryTwice ( > + IN UNIT_TEST_CONTEXT Context > + ) > +{ > + EFI_STATUS Status; > + Status = InitVariablePolicyLib( StubGetVariableNull ); > + UT_ASSERT_NOT_EFI_ERROR( Status ); > + Status = InitVariablePolicyLib( StubGetVariableNull ); > + UT_ASSERT_TRUE( EFI_ERROR( Status ) ); > + return UNIT_TEST_PASSED; > +} > + > +UNIT_TEST_STATUS > +EFIAPI > +ShouldFailDeinitWithoutInit ( > + IN UNIT_TEST_CONTEXT Context > + ) > +{ > + EFI_STATUS Status; > + Status = DeinitVariablePolicyLib(); > + UT_ASSERT_TRUE( EFI_ERROR( Status ) ); > + return UNIT_TEST_PASSED; > +} > + > +UNIT_TEST_STATUS > +EFIAPI > +ApiCommandsShouldNotRespondIfLibIsUninitialized ( > + IN UNIT_TEST_CONTEXT Context > + ) > +{ > + SIMPLE_VARIABLE_POLICY_ENTRY TestPolicy = { > + { > + VARIABLE_POLICY_ENTRY_REVISION, > + sizeof(VARIABLE_POLICY_ENTRY) + sizeof(TEST_VAR_1_NAME), > + sizeof(VARIABLE_POLICY_ENTRY), > + TEST_GUID_1, > + TEST_POLICY_MIN_SIZE_NULL, > + TEST_POLICY_MAX_SIZE_NULL, > + TEST_POLICY_ATTRIBUTES_NULL, > + TEST_POLICY_ATTRIBUTES_NULL, > + VARIABLE_POLICY_TYPE_NO_LOCK > + }, > + TEST_VAR_1_NAME > + }; > + UINT8 DummyData[8]; > + UINT32 DummyDataSize = sizeof(DummyData); > + > + // This test should not start with an initialized library. > + > + // Verify that all API commands fail. > + UT_ASSERT_TRUE( EFI_ERROR( LockVariablePolicy() ) ); > + UT_ASSERT_TRUE( EFI_ERROR( DisableVariablePolicy() ) ); > + > UT_ASSERT_TRUE( EFI_ERROR( RegisterVariablePolicy( &TestPolicy.Header ) > ) ); > + UT_ASSERT_TRUE( EFI_ERROR( DumpVariablePolicy( DummyData, > &DummyDataSize ) ) ); > + UT_ASSERT_FALSE( IsVariablePolicyInterfaceLocked() ); > + UT_ASSERT_FALSE( IsVariablePolicyEnabled() ); > + UT_ASSERT_TRUE( EFI_ERROR( ValidateSetVariable( TEST_VAR_1_NAME, > + &mTestGuid1, > + VARIABLE_ATTRIBUTE_NV_BS, > + sizeof(DummyData), > + DummyData ) ) ); > + > + return UNIT_TEST_PASSED; > +} > + > + > +///===== INTERNAL FUNCTION SUITE > ============================================== > + > +#ifdef INTERNAL_UNIT_TEST > + > +BOOLEAN > +EvaluatePolicyMatch ( > + IN CONST VARIABLE_POLICY_ENTRY *EvalEntry, > + IN CONST CHAR16 *VariableName, > + IN CONST EFI_GUID *VendorGuid, > + OUT UINT8 *MatchPriority OPTIONAL > + ); > + > +UNIT_TEST_STATUS > +EFIAPI > +PoliciesShouldMatchByNameAndGuid ( > + IN UNIT_TEST_CONTEXT Context > + ) > +{ > + SIMPLE_VARIABLE_POLICY_ENTRY MatchCheckPolicy = { > + { > + VARIABLE_POLICY_ENTRY_REVISION, > + sizeof(VARIABLE_POLICY_ENTRY) + sizeof(TEST_VAR_1_NAME), > + sizeof(VARIABLE_POLICY_ENTRY), > + TEST_GUID_1, > + TEST_POLICY_MIN_SIZE_NULL, > + TEST_POLICY_MAX_SIZE_NULL, > + TEST_POLICY_ATTRIBUTES_NULL, > + TEST_POLICY_ATTRIBUTES_NULL, > + VARIABLE_POLICY_TYPE_NO_LOCK > + }, > + TEST_VAR_1_NAME > + }; > + CHAR16 *CheckVar1Name = TEST_VAR_1_NAME; > + CHAR16 *CheckVar2Name = TEST_VAR_2_NAME; > + > + // Make sure that a different name does not match. > + UT_ASSERT_FALSE( EvaluatePolicyMatch( &MatchCheckPolicy.Header, > CheckVar2Name, &mTestGuid1, NULL ) ); > + > + // Make sure that a different GUID does not match. > + UT_ASSERT_FALSE( EvaluatePolicyMatch( &MatchCheckPolicy.Header, > CheckVar1Name, &mTestGuid2, NULL ) ); > + > + // Make sure that the same name and GUID match. > + UT_ASSERT_TRUE( EvaluatePolicyMatch( &MatchCheckPolicy.Header, > CheckVar1Name, &mTestGuid1, NULL ) ); > + > + return UNIT_TEST_PASSED; > +} > + > +UNIT_TEST_STATUS > +EFIAPI > +WildcardPoliciesShouldMatchDigits ( > + IN UNIT_TEST_CONTEXT Context > + ) > +{ > + SIMPLE_VARIABLE_POLICY_ENTRY MatchCheckPolicy = { > + { > + VARIABLE_POLICY_ENTRY_REVISION, > + sizeof(VARIABLE_POLICY_ENTRY) + sizeof(L"Wildcard#VarName##"), > + sizeof(VARIABLE_POLICY_ENTRY), > + TEST_GUID_1, > + TEST_POLICY_MIN_SIZE_NULL, > + TEST_POLICY_MAX_SIZE_NULL, > + TEST_POLICY_ATTRIBUTES_NULL, > + TEST_POLICY_ATTRIBUTES_NULL, > + VARIABLE_POLICY_TYPE_NO_LOCK > + }, > + L"Wildcard#VarName##" > + }; > + CHAR16 *CheckVar1Name = L"Wildcard1VarName12"; > + CHAR16 *CheckVar2Name = L"Wildcard2VarName34"; > + CHAR16 *CheckVarBName = L"WildcardBVarName56"; > + CHAR16 *CheckVarHName = L"Wildcard#VarName56"; > + > + // Make sure that two different sets of wildcard numbers match. > + UT_ASSERT_TRUE( EvaluatePolicyMatch( &MatchCheckPolicy.Header, > CheckVar1Name, &mTestGuid1, NULL ) ); > + UT_ASSERT_TRUE( EvaluatePolicyMatch( &MatchCheckPolicy.Header, > CheckVar2Name, &mTestGuid1, NULL ) ); > + > + // Make sure that the non-number charaters don't match. > + UT_ASSERT_FALSE( EvaluatePolicyMatch( &MatchCheckPolicy.Header, > CheckVarBName, &mTestGuid1, NULL ) ); > + > + // Make sure that '#' signs don't match. > + UT_ASSERT_FALSE( EvaluatePolicyMatch( &MatchCheckPolicy.Header, > CheckVarHName, &mTestGuid1, NULL ) ); > + > + return UNIT_TEST_PASSED; > +} > + > +UNIT_TEST_STATUS > +EFIAPI > +WildcardPoliciesShouldMatchDigitsAdvanced ( > + IN UNIT_TEST_CONTEXT Context > + ) > +{ > + SIMPLE_VARIABLE_POLICY_ENTRY MatchCheckPolicy = { > + { > + VARIABLE_POLICY_ENTRY_REVISION, > + sizeof(VARIABLE_POLICY_ENTRY) + sizeof(TEST_300_HASHES_STRING), > + sizeof(VARIABLE_POLICY_ENTRY), > + TEST_GUID_1, > + TEST_POLICY_MIN_SIZE_NULL, > + TEST_POLICY_MAX_SIZE_NULL, > + TEST_POLICY_ATTRIBUTES_NULL, > + TEST_POLICY_ATTRIBUTES_NULL, > + VARIABLE_POLICY_TYPE_NO_LOCK > + }, > + TEST_300_HASHES_STRING > + }; > + CHAR16 *CheckShorterString = > L"01234567890123456789012345678901234567890123456789"; > + CHAR16 *CheckValidString = > L"01234567890123456789012345678901234567890123456789"\ > + > "01234567890123456789012345678901234567890123456789"\ > + > "01234567890123456789012345678901234567890123456789"\ > + > "01234567890123456789012345678901234567890123456789"\ > + > "01234567890123456789012345678901234567890123456789"\ > + > "01234567890123456789012345678901234567890123456789"; > + CHAR16 *CheckLongerString = > L"01234567890123456789012345678901234567890123456789"\ > + > "01234567890123456789012345678901234567890123456789"\ > + > "01234567890123456789012345678901234567890123456789"\ > + > "01234567890123456789012345678901234567890123456789"\ > + > "01234567890123456789012345678901234567890123456789"\ > + > "01234567890123456789012345678901234567890123456789"\ > + > "01234567890123456789012345678901234567890123456789"; > + UINT8 MatchPriority; > + > + // Make sure that the shorter and the longer do not match. > + UT_ASSERT_FALSE( EvaluatePolicyMatch( &MatchCheckPolicy.Header, > CheckShorterString, &mTestGuid1, NULL ) ); > + UT_ASSERT_FALSE( EvaluatePolicyMatch( &MatchCheckPolicy.Header, > CheckLongerString, &mTestGuid1, NULL ) ); > + > + // Make sure that the valid one matches and has the expected priority. > + UT_ASSERT_TRUE( EvaluatePolicyMatch( &MatchCheckPolicy.Header, > CheckValidString, &mTestGuid1, &MatchPriority ) ); > + UT_ASSERT_EQUAL( MatchPriority, MAX_UINT8 ); > + > + return UNIT_TEST_PASSED; > +} > + > +UNIT_TEST_STATUS > +EFIAPI > +WildcardPoliciesShouldMatchNamespaces ( > + IN UNIT_TEST_CONTEXT Context > + ) > +{ > + VARIABLE_POLICY_ENTRY MatchCheckPolicy = { > + VARIABLE_POLICY_ENTRY_REVISION, > + sizeof(VARIABLE_POLICY_ENTRY), > + sizeof(VARIABLE_POLICY_ENTRY), > + TEST_GUID_1, > + TEST_POLICY_MIN_SIZE_NULL, > + TEST_POLICY_MAX_SIZE_NULL, > + TEST_POLICY_ATTRIBUTES_NULL, > + TEST_POLICY_ATTRIBUTES_NULL, > + VARIABLE_POLICY_TYPE_NO_LOCK > + }; > + CHAR16 *CheckVar1Name = L"Wildcard1VarName12"; > + CHAR16 *CheckVar2Name = L"Wildcard2VarName34"; > + CHAR16 *CheckVarBName = L"WildcardBVarName56"; > + CHAR16 *CheckVarHName = L"Wildcard#VarName56"; > + > + // Make sure that all names in the same namespace match. > + UT_ASSERT_TRUE( EvaluatePolicyMatch( &MatchCheckPolicy, > CheckVar1Name, &mTestGuid1, NULL ) ); > + UT_ASSERT_TRUE( EvaluatePolicyMatch( &MatchCheckPolicy, > CheckVar2Name, &mTestGuid1, NULL ) ); > + UT_ASSERT_TRUE( EvaluatePolicyMatch( &MatchCheckPolicy, > CheckVarBName, &mTestGuid1, NULL ) ); > + UT_ASSERT_TRUE( EvaluatePolicyMatch( &MatchCheckPolicy, > CheckVarHName, &mTestGuid1, NULL ) ); > + > + // Make sure that different namespace doesn't match. > + UT_ASSERT_FALSE( EvaluatePolicyMatch( &MatchCheckPolicy, > CheckVar1Name, &mTestGuid2, NULL ) ); > + > + > + return UNIT_TEST_PASSED; > +} > + > +UNIT_TEST_STATUS > +EFIAPI > +MatchPrioritiesShouldFollowRules ( > + IN UNIT_TEST_CONTEXT Context > + ) > +{ > + SIMPLE_VARIABLE_POLICY_ENTRY MatchCheckPolicy = { > + { > + VARIABLE_POLICY_ENTRY_REVISION, > + sizeof(VARIABLE_POLICY_ENTRY) + sizeof(L"Wildcard1VarName12"), > + sizeof(VARIABLE_POLICY_ENTRY), > + TEST_GUID_1, > + TEST_POLICY_MIN_SIZE_NULL, > + TEST_POLICY_MAX_SIZE_NULL, > + TEST_POLICY_ATTRIBUTES_NULL, > + TEST_POLICY_ATTRIBUTES_NULL, > + VARIABLE_POLICY_TYPE_NO_LOCK > + }, > + L"Wildcard1VarName12" > + }; > + CHAR16 CheckVar1Name[] = L"Wildcard1VarName12"; > + CHAR16 MatchVar1Name[] = L"Wildcard1VarName12"; > + CHAR16 MatchVar2Name[] = L"Wildcard#VarName12"; > + CHAR16 MatchVar3Name[] = L"Wildcard#VarName#2"; > + CHAR16 MatchVar4Name[] = L"Wildcard#VarName##"; > + UINT8 MatchPriority; > + > + // Check with a perfect match. > + CopyMem( &MatchCheckPolicy.Name, MatchVar1Name, > sizeof(MatchVar1Name) ); > + UT_ASSERT_TRUE( EvaluatePolicyMatch( &MatchCheckPolicy.Header, > CheckVar1Name, &mTestGuid1, &MatchPriority ) ); > + UT_ASSERT_EQUAL( MatchPriority, 0 ); > + > + // Check with progressively lower priority matches. > + CopyMem( &MatchCheckPolicy.Name, MatchVar2Name, > sizeof(MatchVar2Name) ); > + UT_ASSERT_TRUE( EvaluatePolicyMatch( &MatchCheckPolicy.Header, > CheckVar1Name, &mTestGuid1, &MatchPriority ) ); > + UT_ASSERT_EQUAL( MatchPriority, 1 ); > + CopyMem( &MatchCheckPolicy.Name, MatchVar3Name, > sizeof(MatchVar3Name) ); > + UT_ASSERT_TRUE( EvaluatePolicyMatch( &MatchCheckPolicy.Header, > CheckVar1Name, &mTestGuid1, &MatchPriority ) ); > + UT_ASSERT_EQUAL( MatchPriority, 2 ); > + CopyMem( &MatchCheckPolicy.Name, MatchVar4Name, > sizeof(MatchVar4Name) ); > + UT_ASSERT_TRUE( EvaluatePolicyMatch( &MatchCheckPolicy.Header, > CheckVar1Name, &mTestGuid1, &MatchPriority ) ); > + UT_ASSERT_EQUAL( MatchPriority, 3 ); > + > + // Check against the entire namespace. > + MatchCheckPolicy.Header.Size = sizeof(VARIABLE_POLICY_ENTRY); > + UT_ASSERT_TRUE( EvaluatePolicyMatch( &MatchCheckPolicy.Header, > CheckVar1Name, &mTestGuid1, &MatchPriority ) ); > + UT_ASSERT_EQUAL( MatchPriority, MAX_UINT8 ); > + > + return UNIT_TEST_PASSED; > +} > + > +#endif // INTERNAL_UNIT_TEST > + > + > +///=== POLICY MANIPULATION SUITE > ============================================== > + > +UNIT_TEST_STATUS > +EFIAPI > +RegisterShouldAllowNamespaceWildcards ( > + IN UNIT_TEST_CONTEXT Context > + ) > +{ > + SIMPLE_VARIABLE_POLICY_ENTRY ValidationPolicy = { > + { > + VARIABLE_POLICY_ENTRY_REVISION, > + sizeof(VARIABLE_POLICY_ENTRY), > + sizeof(VARIABLE_POLICY_ENTRY), > + TEST_GUID_1, > + TEST_POLICY_MIN_SIZE_NULL, > + TEST_POLICY_MAX_SIZE_NULL, > + TEST_POLICY_ATTRIBUTES_NULL, > + TEST_POLICY_ATTRIBUTES_NULL, > + VARIABLE_POLICY_TYPE_NO_LOCK > + }, > + L"" > + }; > + > + > UT_ASSERT_NOT_EFI_ERROR( RegisterVariablePolicy( &ValidationPolicy.Hea > der ) ); > + > + return UNIT_TEST_PASSED; > +} > + > +UNIT_TEST_STATUS > +EFIAPI > +RegisterShouldAllowStateVarsForNamespaces ( > + IN UNIT_TEST_CONTEXT Context > + ) > +{ > + EXPANDED_VARIABLE_POLICY_ENTRY ValidationPolicy = { > + { > + VARIABLE_POLICY_ENTRY_REVISION, > + 0, // Will be populated by init helper. > + 0, // Will be populated by init helper. > + TEST_GUID_1, > + TEST_POLICY_MIN_SIZE_NULL, > + TEST_POLICY_MAX_SIZE_NULL, > + TEST_POLICY_ATTRIBUTES_NULL, > + TEST_POLICY_ATTRIBUTES_NULL, > + VARIABLE_POLICY_TYPE_LOCK_ON_VAR_STATE > + }, > + { > + TEST_GUID_2, > + 1, // Value > + 0 // Padding > + }, > + L"", > + L"" > + }; > + UT_ASSERT_TRUE( InitExpVarPolicyStrings( &ValidationPolicy, NULL, > TEST_VAR_2_NAME ) ); > + > + > UT_ASSERT_NOT_EFI_ERROR( RegisterVariablePolicy( &ValidationPolicy.Hea > der ) ); > + > + return UNIT_TEST_PASSED; > +} > + > +UNIT_TEST_STATUS > +EFIAPI > +RegisterShouldRejectNullPointers ( > + IN UNIT_TEST_CONTEXT Context > + ) > +{ > + UT_ASSERT_EQUAL( RegisterVariablePolicy( NULL ), > EFI_INVALID_PARAMETER ); > + return UNIT_TEST_PASSED; > +} > + > +UNIT_TEST_STATUS > +EFIAPI > +RegisterShouldRejectBadRevisions ( > + IN UNIT_TEST_CONTEXT Context > + ) > +{ > + SIMPLE_VARIABLE_POLICY_ENTRY ValidationPolicy = { > + { > + VARIABLE_POLICY_ENTRY_REVISION, > + sizeof(VARIABLE_POLICY_ENTRY) + sizeof(TEST_VAR_1_NAME), > + sizeof(VARIABLE_POLICY_ENTRY), > + TEST_GUID_1, > + TEST_POLICY_MIN_SIZE_NULL, > + TEST_POLICY_MAX_SIZE_NULL, > + TEST_POLICY_ATTRIBUTES_NULL, > + TEST_POLICY_ATTRIBUTES_NULL, > + VARIABLE_POLICY_TYPE_NO_LOCK > + }, > + TEST_VAR_1_NAME > + }; > + > + ValidationPolicy.Header.Version = MAX_UINT32; > + UT_ASSERT_EQUAL( RegisterVariablePolicy( &ValidationPolicy.Header ), > EFI_INVALID_PARAMETER ); > + > + return UNIT_TEST_PASSED; > +} > + > +UNIT_TEST_STATUS > +EFIAPI > +RegisterShouldRejectBadSizes ( > + IN UNIT_TEST_CONTEXT Context > + ) > +{ > + SIMPLE_VARIABLE_POLICY_ENTRY ValidationPolicy = { > + { > + VARIABLE_POLICY_ENTRY_REVISION, > + sizeof(VARIABLE_POLICY_ENTRY) + sizeof(TEST_VAR_1_NAME), > + sizeof(VARIABLE_POLICY_ENTRY), > + TEST_GUID_1, > + TEST_POLICY_MIN_SIZE_NULL, > + TEST_POLICY_MAX_SIZE_NULL, > + TEST_POLICY_ATTRIBUTES_NULL, > + TEST_POLICY_ATTRIBUTES_NULL, > + VARIABLE_POLICY_TYPE_NO_LOCK > + }, > + TEST_VAR_1_NAME > + }; > + > + ValidationPolicy.Header.Size = sizeof(VARIABLE_POLICY_ENTRY) - 2; > + UT_ASSERT_EQUAL( RegisterVariablePolicy( &ValidationPolicy.Header ), > EFI_INVALID_PARAMETER ); > + > + return UNIT_TEST_PASSED; > +} > + > +UNIT_TEST_STATUS > +EFIAPI > +RegisterShouldRejectBadOffsets ( > + IN UNIT_TEST_CONTEXT Context > + ) > +{ > + EXPANDED_VARIABLE_POLICY_ENTRY ValidationPolicy = { > + { > + VARIABLE_POLICY_ENTRY_REVISION, > + 0, // Will be populated by init helper. > + 0, // Will be populated by init helper. > + TEST_GUID_1, > + TEST_POLICY_MIN_SIZE_NULL, > + TEST_POLICY_MAX_SIZE_NULL, > + TEST_POLICY_ATTRIBUTES_NULL, > + TEST_POLICY_ATTRIBUTES_NULL, > + VARIABLE_POLICY_TYPE_LOCK_ON_VAR_STATE > + }, > + { > + TEST_GUID_2, > + 1, // Value > + 0 // Padding > + }, > + L"", > + L"" > + }; > + UT_ASSERT_TRUE( InitExpVarPolicyStrings( &ValidationPolicy, > TEST_VAR_1_NAME, TEST_VAR_2_NAME ) ); > + > + // Check for an offset outside the size bounds. > + ValidationPolicy.Header.OffsetToName = ValidationPolicy.Header.Size + 1; > + UT_ASSERT_EQUAL( RegisterVariablePolicy( &ValidationPolicy.Header ), > EFI_INVALID_PARAMETER ); > + > + // Check for an offset inside the policy header. > + ValidationPolicy.Header.OffsetToName = sizeof(VARIABLE_POLICY_ENTRY) > - 2; > + UT_ASSERT_EQUAL( RegisterVariablePolicy( &ValidationPolicy.Header ), > EFI_INVALID_PARAMETER ); > + > + // Check for an offset inside the state policy header. > + ValidationPolicy.Header.OffsetToName = sizeof(VARIABLE_POLICY_ENTRY) > + 2; > + UT_ASSERT_EQUAL( RegisterVariablePolicy( &ValidationPolicy.Header ), > EFI_INVALID_PARAMETER ); > + > + // Check for a ridiculous offset. > + ValidationPolicy.Header.OffsetToName = MAX_UINT16; > + UT_ASSERT_EQUAL( RegisterVariablePolicy( &ValidationPolicy.Header ), > EFI_INVALID_PARAMETER ); > + > + return UNIT_TEST_PASSED; > +} > + > +UNIT_TEST_STATUS > +EFIAPI > +RegisterShouldRejectMissingStateStrings ( > + IN UNIT_TEST_CONTEXT Context > + ) > +{ > + EXPANDED_VARIABLE_POLICY_ENTRY ValidationPolicy = { > + { > + VARIABLE_POLICY_ENTRY_REVISION, > + 0, // Will be populated by init helper. > + 0, // Will be populated by init helper. > + TEST_GUID_1, > + TEST_POLICY_MIN_SIZE_NULL, > + TEST_POLICY_MAX_SIZE_NULL, > + TEST_POLICY_ATTRIBUTES_NULL, > + TEST_POLICY_ATTRIBUTES_NULL, > + VARIABLE_POLICY_TYPE_LOCK_ON_VAR_STATE > + }, > + { > + TEST_GUID_2, > + 1, // Value > + 0 // Padding > + }, > + L"", > + L"" > + }; > + UT_ASSERT_TRUE( InitExpVarPolicyStrings( &ValidationPolicy, > TEST_VAR_1_NAME, TEST_VAR_2_NAME ) ); > + > + // Remove the state string and copy the Name into it's place. > + // Also adjust the offset. > + ValidationPolicy.Header.Size = sizeof(VARIABLE_POLICY_ENTRY) + > sizeof(VARIABLE_LOCK_ON_VAR_STATE_POLICY) + > sizeof(TEST_VAR_1_NAME); > + ValidationPolicy.Header.OffsetToName = sizeof(VARIABLE_POLICY_ENTRY) > + sizeof(VARIABLE_LOCK_ON_VAR_STATE_POLICY); > + CopyMem( (UINT8*)&ValidationPolicy + > ValidationPolicy.Header.OffsetToName, TEST_VAR_1_NAME, > sizeof(TEST_VAR_1_NAME) ); > + > + // Make sure that this structure fails. > + UT_ASSERT_EQUAL( RegisterVariablePolicy( &ValidationPolicy.Header ), > EFI_INVALID_PARAMETER ); > + > + return UNIT_TEST_PASSED; > +} > + > +UNIT_TEST_STATUS > +EFIAPI > +RegisterShouldRejectStringsMissingNull ( > + IN UNIT_TEST_CONTEXT Context > + ) > +{ > + EXPANDED_VARIABLE_POLICY_ENTRY ValidationPolicy = { > + { > + VARIABLE_POLICY_ENTRY_REVISION, > + 0, // Will be populated by init helper. > + 0, // Will be populated by init helper. > + TEST_GUID_1, > + TEST_POLICY_MIN_SIZE_NULL, > + TEST_POLICY_MAX_SIZE_NULL, > + TEST_POLICY_ATTRIBUTES_NULL, > + TEST_POLICY_ATTRIBUTES_NULL, > + VARIABLE_POLICY_TYPE_LOCK_ON_VAR_STATE > + }, > + { > + TEST_GUID_2, > + 1, // Value > + 0 // Padding > + }, > + L"", > + L"" > + }; > + UT_ASSERT_TRUE( InitExpVarPolicyStrings( &ValidationPolicy, > TEST_VAR_1_NAME, TEST_VAR_2_NAME ) ); > + > + // Removing the NULL from the Name should fail. > + ValidationPolicy.Header.Size = ValidationPolicy.Header.Size - > sizeof(CHAR16); > + UT_ASSERT_EQUAL( RegisterVariablePolicy( &ValidationPolicy.Header ), > EFI_INVALID_PARAMETER ); > + > + // Removing the NULL from the State Name is a little trickier. > + // Copy the Name up one byte. > + ValidationPolicy.Header.OffsetToName = > ValidationPolicy.Header.OffsetToName - sizeof(CHAR16); > + CopyMem( (UINT8*)&ValidationPolicy + > ValidationPolicy.Header.OffsetToName, TEST_VAR_1_NAME, > sizeof(TEST_VAR_1_NAME) ); > + UT_ASSERT_EQUAL( RegisterVariablePolicy( &ValidationPolicy.Header ), > EFI_INVALID_PARAMETER ); > + > + return UNIT_TEST_PASSED; > +} > + > +UNIT_TEST_STATUS > +EFIAPI > +RegisterShouldRejectMalformedStrings ( > + IN UNIT_TEST_CONTEXT Context > + ) > +{ > + EXPANDED_VARIABLE_POLICY_ENTRY ValidationPolicy = { > + { > + VARIABLE_POLICY_ENTRY_REVISION, > + 0, // Will be populated by init helper. > + 0, // Will be populated by init helper. > + TEST_GUID_1, > + TEST_POLICY_MIN_SIZE_NULL, > + TEST_POLICY_MAX_SIZE_NULL, > + TEST_POLICY_ATTRIBUTES_NULL, > + TEST_POLICY_ATTRIBUTES_NULL, > + VARIABLE_POLICY_TYPE_LOCK_ON_VAR_STATE > + }, > + { > + TEST_GUID_2, > + 1, // Value > + 0 // Padding > + }, > + L"", > + L"" > + }; > + UT_ASSERT_TRUE( InitExpVarPolicyStrings( &ValidationPolicy, > TEST_VAR_1_NAME, TEST_VAR_2_NAME ) ); > + > + // Bisecting the NULL from the Name should fail. > + ValidationPolicy.Header.Size = ValidationPolicy.Header.Size - 1; > + UT_ASSERT_EQUAL( RegisterVariablePolicy( &ValidationPolicy.Header ), > EFI_INVALID_PARAMETER ); > + > + // Bisecting the NULL from the State Name is a little trickier. > + // Copy the Name up one byte. > + ValidationPolicy.Header.OffsetToName = > ValidationPolicy.Header.OffsetToName - 1; > + CopyMem( (UINT8*)&ValidationPolicy + > ValidationPolicy.Header.OffsetToName, TEST_VAR_1_NAME, > sizeof(TEST_VAR_1_NAME) ); > + UT_ASSERT_EQUAL( RegisterVariablePolicy( &ValidationPolicy.Header ), > EFI_INVALID_PARAMETER ); > + > + return UNIT_TEST_PASSED; > +} > + > +UNIT_TEST_STATUS > +EFIAPI > +RegisterShouldRejectUnpackedPolicies ( > + IN UNIT_TEST_CONTEXT Context > + ) > +{ > + EXPANDED_VARIABLE_POLICY_ENTRY ValidationPolicy = { > + { > + VARIABLE_POLICY_ENTRY_REVISION, > + 0, // Will be populated by init helper. > + 0, // Will be populated by init helper. > + TEST_GUID_1, > + TEST_POLICY_MIN_SIZE_NULL, > + TEST_POLICY_MAX_SIZE_NULL, > + TEST_POLICY_ATTRIBUTES_NULL, > + TEST_POLICY_ATTRIBUTES_NULL, > + VARIABLE_POLICY_TYPE_LOCK_ON_VAR_STATE > + }, > + { > + TEST_GUID_2, > + 1, // Value > + 0 // Padding > + }, > + L"", > + L"" > + }; > + UT_ASSERT_TRUE( InitExpVarPolicyStrings( &ValidationPolicy, > TEST_VAR_1_NAME, TEST_VAR_2_NAME ) ); > + > + // Increase the size and move the Name out a bit. > + ValidationPolicy.Header.Size = ValidationPolicy.Header.Size + > sizeof(CHAR16); > + ValidationPolicy.Header.OffsetToName = > ValidationPolicy.Header.OffsetToName + sizeof(CHAR16); > + CopyMem( (UINT8*)&ValidationPolicy + > ValidationPolicy.Header.OffsetToName, TEST_VAR_1_NAME, > sizeof(TEST_VAR_1_NAME) ); > + UT_ASSERT_EQUAL( RegisterVariablePolicy( &ValidationPolicy.Header ), > EFI_INVALID_PARAMETER ); > + > + // Reintialize without the state policy and try the same test. > + ValidationPolicy.Header.LockPolicyType = > VARIABLE_POLICY_TYPE_NO_LOCK; > + UT_ASSERT_TRUE( InitExpVarPolicyStrings( &ValidationPolicy, > TEST_VAR_1_NAME, NULL ) ); > + ValidationPolicy.Header.Size = ValidationPolicy.Header.Size + > sizeof(CHAR16); > + ValidationPolicy.Header.OffsetToName = > ValidationPolicy.Header.OffsetToName + sizeof(CHAR16); > + CopyMem( (UINT8*)&ValidationPolicy + > ValidationPolicy.Header.OffsetToName, TEST_VAR_1_NAME, > sizeof(TEST_VAR_1_NAME) ); > + UT_ASSERT_EQUAL( RegisterVariablePolicy( &ValidationPolicy.Header ), > EFI_INVALID_PARAMETER ); > + > + return UNIT_TEST_PASSED; > +} > + > +UNIT_TEST_STATUS > +EFIAPI > +RegisterShouldRejectInvalidNameCharacters ( > + IN UNIT_TEST_CONTEXT Context > + ) > +{ > + // EXPANDED_VARIABLE_POLICY_ENTRY ValidationPolicy = { > + // { > + // VARIABLE_POLICY_ENTRY_REVISION, > + // 0, // Will be populated by init helper. > + // 0, // Will be populated by init helper. > + // TEST_GUID_1, > + // TEST_POLICY_MIN_SIZE_NULL, > + // TEST_POLICY_MAX_SIZE_NULL, > + // TEST_POLICY_ATTRIBUTES_NULL, > + // TEST_POLICY_ATTRIBUTES_NULL, > + // VARIABLE_POLICY_TYPE_LOCK_ON_VAR_STATE > + // }, > + // { > + // TEST_GUID_2, > + // 1, // Value > + // 0 // Padding > + // }, > + // L"", > + // L"" > + // }; > + > + // Currently, there are no known invalid characters. > + // '#' in LockPolicy->Name are taken as literal. > + > + return UNIT_TEST_PASSED; > +} > + > +UNIT_TEST_STATUS > +EFIAPI > +RegisterShouldRejectBadPolicyConstraints ( > + IN UNIT_TEST_CONTEXT Context > + ) > +{ > + SIMPLE_VARIABLE_POLICY_ENTRY ValidationPolicy = { > + { > + VARIABLE_POLICY_ENTRY_REVISION, > + sizeof(VARIABLE_POLICY_ENTRY) + sizeof(TEST_VAR_1_NAME), > + sizeof(VARIABLE_POLICY_ENTRY), > + TEST_GUID_1, > + TEST_POLICY_MIN_SIZE_NULL, > + TEST_POLICY_MAX_SIZE_NULL, > + TEST_POLICY_ATTRIBUTES_NULL, > + TEST_POLICY_ATTRIBUTES_NULL, > + VARIABLE_POLICY_TYPE_NO_LOCK > + }, > + TEST_VAR_1_NAME > + }; > + > + // Make sure that invalid MAXes are rejected. > + ValidationPolicy.Header.MaxSize = 0; > + UT_ASSERT_EQUAL( RegisterVariablePolicy( &ValidationPolicy.Header ), > EFI_INVALID_PARAMETER ); > + > + return UNIT_TEST_PASSED; > +} > + > +UNIT_TEST_STATUS > +EFIAPI > +RegisterShouldRejectUnknownLockPolicies ( > + IN UNIT_TEST_CONTEXT Context > + ) > +{ > + SIMPLE_VARIABLE_POLICY_ENTRY ValidationPolicy = { > + { > + VARIABLE_POLICY_ENTRY_REVISION, > + sizeof(VARIABLE_POLICY_ENTRY) + sizeof(TEST_VAR_1_NAME), > + sizeof(VARIABLE_POLICY_ENTRY), > + TEST_GUID_1, > + TEST_POLICY_MIN_SIZE_NULL, > + TEST_POLICY_MAX_SIZE_NULL, > + TEST_POLICY_ATTRIBUTES_NULL, > + TEST_POLICY_ATTRIBUTES_NULL, > + VARIABLE_POLICY_TYPE_NO_LOCK > + }, > + TEST_VAR_1_NAME > + }; > + > + ValidationPolicy.Header.LockPolicyType = > VARIABLE_POLICY_TYPE_LOCK_ON_VAR_STATE + 1; > + UT_ASSERT_EQUAL( RegisterVariablePolicy( &ValidationPolicy.Header ), > EFI_INVALID_PARAMETER ); > + ValidationPolicy.Header.LockPolicyType = > VARIABLE_POLICY_TYPE_LOCK_ON_VAR_STATE + 1; > + UT_ASSERT_EQUAL( RegisterVariablePolicy( &ValidationPolicy.Header ), > EFI_INVALID_PARAMETER ); > + > + return UNIT_TEST_PASSED; > +} > + > +UNIT_TEST_STATUS > +EFIAPI > +RegisterShouldRejectPolicesWithTooManyWildcards ( > + IN UNIT_TEST_CONTEXT Context > + ) > +{ > + SIMPLE_VARIABLE_POLICY_ENTRY ValidationPolicy = { > + { > + VARIABLE_POLICY_ENTRY_REVISION, > + sizeof(VARIABLE_POLICY_ENTRY) + sizeof(TEST_300_HASHES_STRING), > + sizeof(VARIABLE_POLICY_ENTRY), > + TEST_GUID_1, > + TEST_POLICY_MIN_SIZE_NULL, > + TEST_POLICY_MAX_SIZE_NULL, > + TEST_POLICY_ATTRIBUTES_NULL, > + TEST_POLICY_ATTRIBUTES_NULL, > + VARIABLE_POLICY_TYPE_NO_LOCK > + }, > + TEST_300_HASHES_STRING > + }; > + > + // 300 Hashes is currently larger than the possible maximum match priority. > + UT_ASSERT_EQUAL( RegisterVariablePolicy( &ValidationPolicy.Header ), > EFI_INVALID_PARAMETER ); > + > + return UNIT_TEST_PASSED; > +} > + > +UNIT_TEST_STATUS > +EFIAPI > +RegisterShouldRejectDuplicatePolicies ( > + IN UNIT_TEST_CONTEXT Context > + ) > +{ > + SIMPLE_VARIABLE_POLICY_ENTRY ValidationPolicy = { > + { > + VARIABLE_POLICY_ENTRY_REVISION, > + sizeof(VARIABLE_POLICY_ENTRY) + sizeof(TEST_VAR_1_NAME), > + sizeof(VARIABLE_POLICY_ENTRY), > + TEST_GUID_1, > + TEST_POLICY_MIN_SIZE_NULL, > + TEST_POLICY_MAX_SIZE_NULL, > + TEST_POLICY_ATTRIBUTES_NULL, > + TEST_POLICY_ATTRIBUTES_NULL, > + VARIABLE_POLICY_TYPE_NO_LOCK > + }, > + TEST_VAR_1_NAME > + }; > + > + > UT_ASSERT_NOT_EFI_ERROR( RegisterVariablePolicy( &ValidationPolicy.Hea > der ) ); > + > UT_ASSERT_STATUS_EQUAL( RegisterVariablePolicy( &ValidationPolicy.Head > er ), EFI_ALREADY_STARTED ); > + > + return UNIT_TEST_PASSED; > +} > + > +UNIT_TEST_STATUS > +EFIAPI > +MinAndMaxSizePoliciesShouldBeHonored ( > + IN UNIT_TEST_CONTEXT Context > + ) > +{ > + SIMPLE_VARIABLE_POLICY_ENTRY ValidationPolicy = { > + { > + VARIABLE_POLICY_ENTRY_REVISION, > + sizeof(VARIABLE_POLICY_ENTRY) + sizeof(TEST_VAR_1_NAME), > + sizeof(VARIABLE_POLICY_ENTRY), > + TEST_GUID_1, > + TEST_POLICY_MIN_SIZE_10, > + TEST_POLICY_MAX_SIZE_200, > + TEST_POLICY_ATTRIBUTES_NULL, > + TEST_POLICY_ATTRIBUTES_NULL, > + VARIABLE_POLICY_TYPE_NO_LOCK > + }, > + TEST_VAR_1_NAME > + }; > + EFI_STATUS PolicyCheck; > + UINT8 DummyData[TEST_POLICY_MAX_SIZE_200+1]; > + > + > + // Without a policy, there should be no constraints on variable creation. > + PolicyCheck = ValidateSetVariable( TEST_VAR_1_NAME, > + &mTestGuid1, > + VARIABLE_ATTRIBUTE_NV_BS, > + TEST_POLICY_MAX_SIZE_200+1, > + DummyData ); > + UT_ASSERT_NOT_EFI_ERROR( PolicyCheck ); > + > + // Set a policy to test against. > + > UT_ASSERT_NOT_EFI_ERROR( RegisterVariablePolicy( &ValidationPolicy.Hea > der ) ); > + > + // With a policy, make sure that sizes outsize the target range fail. > + PolicyCheck = ValidateSetVariable( TEST_VAR_1_NAME, > + &mTestGuid1, > + VARIABLE_ATTRIBUTE_NV_BS, > + TEST_POLICY_MAX_SIZE_200+1, > + DummyData ); > + UT_ASSERT_TRUE( EFI_ERROR( PolicyCheck ) ); > + > + // With a policy, make sure that sizes outsize the target range fail. > + PolicyCheck = ValidateSetVariable( TEST_VAR_1_NAME, > + &mTestGuid1, > + VARIABLE_ATTRIBUTE_NV_BS, > + TEST_POLICY_MIN_SIZE_10-1, > + DummyData ); > + UT_ASSERT_TRUE( EFI_ERROR( PolicyCheck ) ); > + > + // With a policy, make sure a valid variable is still valid. > + PolicyCheck = ValidateSetVariable( TEST_VAR_1_NAME, > + &mTestGuid1, > + VARIABLE_ATTRIBUTE_NV_BS, > + TEST_POLICY_MIN_SIZE_10+1, > + DummyData ); > + UT_ASSERT_NOT_EFI_ERROR( PolicyCheck ); > + > + return UNIT_TEST_PASSED; > +} > + > +UNIT_TEST_STATUS > +EFIAPI > +AttributeMustPoliciesShouldBeHonored ( > + IN UNIT_TEST_CONTEXT Context > + ) > +{ > + SIMPLE_VARIABLE_POLICY_ENTRY ValidationPolicy = { > + { > + VARIABLE_POLICY_ENTRY_REVISION, > + sizeof(VARIABLE_POLICY_ENTRY) + sizeof(TEST_VAR_1_NAME), > + sizeof(VARIABLE_POLICY_ENTRY), > + TEST_GUID_1, > + TEST_POLICY_MIN_SIZE_NULL, > + TEST_POLICY_MAX_SIZE_NULL, > + VARIABLE_ATTRIBUTE_NV_BS_RT, > + TEST_POLICY_ATTRIBUTES_NULL, > + VARIABLE_POLICY_TYPE_NO_LOCK > + }, > + TEST_VAR_1_NAME > + }; > + EFI_STATUS PolicyCheck; > + UINT8 DummyData[12]; > + > + > + // Without a policy, there should be no constraints on variable creation. > + PolicyCheck = ValidateSetVariable( TEST_VAR_1_NAME, > + &mTestGuid1, > + TEST_POLICY_ATTRIBUTES_NULL, > + sizeof(DummyData), > + DummyData ); > + UT_ASSERT_NOT_EFI_ERROR( PolicyCheck ); > + > + // Set a policy to test against. > + > UT_ASSERT_NOT_EFI_ERROR( RegisterVariablePolicy( &ValidationPolicy.Hea > der ) ); > + > + // With a policy, make sure that no attributes fail. > + PolicyCheck = ValidateSetVariable( TEST_VAR_1_NAME, > + &mTestGuid1, > + TEST_POLICY_ATTRIBUTES_NULL, > + sizeof(DummyData), > + DummyData ); > + UT_ASSERT_TRUE( EFI_ERROR( PolicyCheck ) ); > + > + // With a policy, make sure that some -- but not all -- attributes fail. > + PolicyCheck = ValidateSetVariable( TEST_VAR_1_NAME, > + &mTestGuid1, > + VARIABLE_ATTRIBUTE_BS_RT_AT, > + sizeof(DummyData), > + DummyData ); > + UT_ASSERT_TRUE( EFI_ERROR( PolicyCheck ) ); > + > + // With a policy, make sure that all attributes pass. > + PolicyCheck = ValidateSetVariable( TEST_VAR_1_NAME, > + &mTestGuid1, > + VARIABLE_ATTRIBUTE_NV_BS_RT, > + sizeof(DummyData), > + DummyData ); > + UT_ASSERT_NOT_EFI_ERROR( PolicyCheck ); > + > + // With a policy, make sure that all attributes -- plus some -- pass. > + PolicyCheck = ValidateSetVariable( TEST_VAR_1_NAME, > + &mTestGuid1, > + VARIABLE_ATTRIBUTE_NV_BS_RT_AT, > + sizeof(DummyData), > + DummyData ); > + UT_ASSERT_NOT_EFI_ERROR( PolicyCheck ); > + > + return UNIT_TEST_PASSED; > +} > + > +UNIT_TEST_STATUS > +EFIAPI > +AttributeCantPoliciesShouldBeHonored ( > + IN UNIT_TEST_CONTEXT Context > + ) > +{ > + SIMPLE_VARIABLE_POLICY_ENTRY ValidationPolicy = { > + { > + VARIABLE_POLICY_ENTRY_REVISION, > + sizeof(VARIABLE_POLICY_ENTRY) + sizeof(TEST_VAR_1_NAME), > + sizeof(VARIABLE_POLICY_ENTRY), > + TEST_GUID_1, > + TEST_POLICY_MIN_SIZE_NULL, > + TEST_POLICY_MAX_SIZE_NULL, > + TEST_POLICY_ATTRIBUTES_NULL, > + EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS, > + VARIABLE_POLICY_TYPE_NO_LOCK > + }, > + TEST_VAR_1_NAME > + }; > + EFI_STATUS PolicyCheck; > + UINT8 DummyData[12]; > + > + > + // Without a policy, there should be no constraints on variable creation. > + PolicyCheck = ValidateSetVariable( TEST_VAR_1_NAME, > + &mTestGuid1, > + VARIABLE_ATTRIBUTE_BS_RT_AT, > + sizeof(DummyData), > + DummyData ); > + UT_ASSERT_NOT_EFI_ERROR( PolicyCheck ); > + > + // Set a policy to test against. > + > UT_ASSERT_NOT_EFI_ERROR( RegisterVariablePolicy( &ValidationPolicy.Hea > der ) ); > + > + // With a policy, make sure that forbidden attributes fail. > + PolicyCheck = ValidateSetVariable( TEST_VAR_1_NAME, > + &mTestGuid1, > + > EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS, > + sizeof(DummyData), > + DummyData ); > + UT_ASSERT_TRUE( EFI_ERROR( PolicyCheck ) ); > + > + // With a policy, make sure that a mixture of attributes -- including the > forbidden -- fail. > + PolicyCheck = ValidateSetVariable( TEST_VAR_1_NAME, > + &mTestGuid1, > + VARIABLE_ATTRIBUTE_BS_RT_AT, > + sizeof(DummyData), > + DummyData ); > + UT_ASSERT_TRUE( EFI_ERROR( PolicyCheck ) ); > + > + // With a policy, make sure that attributes without the forbidden pass. > + PolicyCheck = ValidateSetVariable( TEST_VAR_1_NAME, > + &mTestGuid1, > + VARIABLE_ATTRIBUTE_NV_BS_RT, > + sizeof(DummyData), > + DummyData ); > + UT_ASSERT_NOT_EFI_ERROR( PolicyCheck ); > + > + return UNIT_TEST_PASSED; > +} > + > +UNIT_TEST_STATUS > +EFIAPI > +VariablesShouldBeDeletableRegardlessOfSize ( > + IN UNIT_TEST_CONTEXT Context > + ) > +{ > + SIMPLE_VARIABLE_POLICY_ENTRY ValidationPolicy = { > + { > + VARIABLE_POLICY_ENTRY_REVISION, > + sizeof(VARIABLE_POLICY_ENTRY) + sizeof(TEST_VAR_1_NAME), > + sizeof(VARIABLE_POLICY_ENTRY), > + TEST_GUID_1, > + TEST_POLICY_MIN_SIZE_10, > + TEST_POLICY_MAX_SIZE_NULL, > + TEST_POLICY_ATTRIBUTES_NULL, > + EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS, > + VARIABLE_POLICY_TYPE_NO_LOCK > + }, > + TEST_VAR_1_NAME > + }; > + EFI_STATUS PolicyCheck; > + UINT8 DummyData[TEST_POLICY_MAX_SIZE_200+1]; > + > + // Create a policy enforcing a minimum variable size. > + > UT_ASSERT_NOT_EFI_ERROR( RegisterVariablePolicy( &ValidationPolicy.Hea > der ) ); > + > + // Make sure that a normal set would fail. > + PolicyCheck = ValidateSetVariable( TEST_VAR_1_NAME, > + &mTestGuid1, > + VARIABLE_ATTRIBUTE_NV_BS, > + TEST_POLICY_MIN_SIZE_10-1, > + DummyData ); > + UT_ASSERT_STATUS_EQUAL( PolicyCheck, EFI_INVALID_PARAMETER ); > + > + // Now make sure that a delete would succeed. > + PolicyCheck = ValidateSetVariable( TEST_VAR_1_NAME, > + &mTestGuid1, > + VARIABLE_ATTRIBUTE_NV_BS, > + 0, > + NULL ); > + UT_ASSERT_NOT_EFI_ERROR( PolicyCheck ); > + > + return UNIT_TEST_PASSED; > +} > + > +UNIT_TEST_STATUS > +EFIAPI > +LockNowPoliciesShouldBeHonored ( > + IN UNIT_TEST_CONTEXT Context > + ) > +{ > + SIMPLE_VARIABLE_POLICY_ENTRY ValidationPolicy = { > + { > + VARIABLE_POLICY_ENTRY_REVISION, > + sizeof(VARIABLE_POLICY_ENTRY) + sizeof(TEST_VAR_1_NAME), > + sizeof(VARIABLE_POLICY_ENTRY), > + TEST_GUID_1, > + TEST_POLICY_MIN_SIZE_NULL, > + TEST_POLICY_MAX_SIZE_NULL, > + TEST_POLICY_ATTRIBUTES_NULL, > + TEST_POLICY_ATTRIBUTES_NULL, > + VARIABLE_POLICY_TYPE_LOCK_NOW > + }, > + TEST_VAR_1_NAME > + }; > + EFI_STATUS PolicyCheck; > + UINT8 DummyData[12]; > + > + > + // Without a policy, there should be no constraints on variable creation. > + PolicyCheck = ValidateSetVariable( TEST_VAR_1_NAME, > + &mTestGuid1, > + VARIABLE_ATTRIBUTE_BS_RT_AT, > + sizeof(DummyData), > + DummyData ); > + UT_ASSERT_NOT_EFI_ERROR( PolicyCheck ); > + > + // Set a policy to test against. > + > UT_ASSERT_NOT_EFI_ERROR( RegisterVariablePolicy( &ValidationPolicy.Hea > der ) ); > + > + // With a policy, make sure that writes immediately fail. > + PolicyCheck = ValidateSetVariable( TEST_VAR_1_NAME, > + &mTestGuid1, > + VARIABLE_ATTRIBUTE_BS_RT_AT, > + sizeof(DummyData), > + DummyData ); > + UT_ASSERT_TRUE( EFI_ERROR( PolicyCheck ) ); > + > + return UNIT_TEST_PASSED; > +} > + > +UNIT_TEST_STATUS > +EFIAPI > +LockOnCreatePoliciesShouldBeHonored ( > + IN UNIT_TEST_CONTEXT Context > + ) > +{ > + SIMPLE_VARIABLE_POLICY_ENTRY ValidationPolicy = { > + { > + VARIABLE_POLICY_ENTRY_REVISION, > + sizeof(VARIABLE_POLICY_ENTRY) + sizeof(TEST_VAR_1_NAME), > + sizeof(VARIABLE_POLICY_ENTRY), > + TEST_GUID_1, > + TEST_POLICY_MIN_SIZE_NULL, > + TEST_POLICY_MAX_SIZE_NULL, > + TEST_POLICY_ATTRIBUTES_NULL, > + TEST_POLICY_ATTRIBUTES_NULL, > + VARIABLE_POLICY_TYPE_LOCK_ON_CREATE > + }, > + TEST_VAR_1_NAME > + }; > + EFI_STATUS PolicyCheck; > + UINT8 DummyData[12]; > + UINTN ExpectedDataSize; > + > + > + // Without a policy, there should be no constraints on variable creation. > + PolicyCheck = ValidateSetVariable( TEST_VAR_1_NAME, > + &mTestGuid1, > + VARIABLE_ATTRIBUTE_BS_RT_AT, > + sizeof(DummyData), > + DummyData ); > + UT_ASSERT_NOT_EFI_ERROR( PolicyCheck ); > + > + // Set a policy to test against. > + > UT_ASSERT_NOT_EFI_ERROR( RegisterVariablePolicy( &ValidationPolicy.Hea > der ) ); > + > + // Set consistent expectations on what the calls are looking for. > + expect_memory_count( StubGetVariableNull, VariableName, > TEST_VAR_1_NAME, sizeof(TEST_VAR_1_NAME), 2 ); > + expect_memory_count( StubGetVariableNull, VendorGuid, &mTestGuid1, > sizeof(mTestGuid1), 2 ); > + ExpectedDataSize = 0; > + expect_memory_count( StubGetVariableNull, DataSize, > &ExpectedDataSize, sizeof(ExpectedDataSize), 2 ); > + > + // With a policy, make sure that writes still work, since the variable doesn't > exist. > + will_return( StubGetVariableNull, TEST_POLICY_ATTRIBUTES_NULL ); // > Attributes > + will_return( StubGetVariableNull, 0 ); // Size > + will_return( StubGetVariableNull, NULL ); // DataPtr > + will_return( StubGetVariableNull, EFI_NOT_FOUND ); // Status > + PolicyCheck = ValidateSetVariable( TEST_VAR_1_NAME, > + &mTestGuid1, > + VARIABLE_ATTRIBUTE_BS_RT_AT, > + sizeof(DummyData), > + DummyData ); > + UT_ASSERT_NOT_EFI_ERROR( PolicyCheck ); > + > + // With a policy, make sure that a call with an "existing" variable fails. > + will_return( StubGetVariableNull, TEST_POLICY_ATTRIBUTES_NULL ); // > Attributes > + will_return( StubGetVariableNull, 10 ); // Size > + will_return( StubGetVariableNull, NULL ); // DataPtr > + will_return( StubGetVariableNull, EFI_BUFFER_TOO_SMALL ); // > Status > + PolicyCheck = ValidateSetVariable( TEST_VAR_1_NAME, > + &mTestGuid1, > + VARIABLE_ATTRIBUTE_BS_RT_AT, > + sizeof(DummyData), > + DummyData ); > + UT_ASSERT_TRUE( EFI_ERROR( PolicyCheck ) ); > + > + return UNIT_TEST_PASSED; > +} > + > +UNIT_TEST_STATUS > +EFIAPI > +LockOnStatePoliciesShouldBeHonored ( > + IN UNIT_TEST_CONTEXT Context > + ) > +{ > + EXPANDED_VARIABLE_POLICY_ENTRY ValidationPolicy = { > + { > + VARIABLE_POLICY_ENTRY_REVISION, > + 0, // Will be populated by init helper. > + 0, // Will be populated by init helper. > + TEST_GUID_1, > + TEST_POLICY_MIN_SIZE_NULL, > + TEST_POLICY_MAX_SIZE_NULL, > + TEST_POLICY_ATTRIBUTES_NULL, > + TEST_POLICY_ATTRIBUTES_NULL, > + VARIABLE_POLICY_TYPE_LOCK_ON_VAR_STATE > + }, > + { > + TEST_GUID_2, > + 20, // Value > + 0 // Padding > + }, > + L"", > + L"" > + }; > + EFI_STATUS PolicyCheck; > + UINT8 DummyData[12]; > + UINT8 ValidationStateVar; > + UINTN ExpectedDataSize; > + UT_ASSERT_TRUE( InitExpVarPolicyStrings( &ValidationPolicy, > TEST_VAR_1_NAME, TEST_VAR_2_NAME ) ); > + > + > + // Without a policy, there should be no constraints on variable creation. > + PolicyCheck = ValidateSetVariable( TEST_VAR_1_NAME, > + &mTestGuid1, > + VARIABLE_ATTRIBUTE_BS_RT_AT, > + sizeof(DummyData), > + DummyData ); > + UT_ASSERT_NOT_EFI_ERROR( PolicyCheck ); > + > + // Set a policy to test against. > + > UT_ASSERT_NOT_EFI_ERROR( RegisterVariablePolicy( &ValidationPolicy.Hea > der ) ); > + > + // Set consistent expectations on what the calls are looking for. > + expect_memory_count( StubGetVariableNull, VariableName, > TEST_VAR_2_NAME, sizeof(TEST_VAR_2_NAME), 5 ); > + expect_memory_count( StubGetVariableNull, VendorGuid, &mTestGuid2, > sizeof(mTestGuid2), 5 ); > + ExpectedDataSize = 1; > + expect_memory_count( StubGetVariableNull, DataSize, > &ExpectedDataSize, sizeof(ExpectedDataSize), 5 ); > + > + // With a policy, make sure that writes still work, since the variable doesn't > exist. > + will_return( StubGetVariableNull, TEST_POLICY_ATTRIBUTES_NULL ); // > Attributes > + will_return( StubGetVariableNull, 0 ); // Size > + will_return( StubGetVariableNull, NULL ); // DataPtr > + will_return( StubGetVariableNull, EFI_NOT_FOUND ); // Status > + PolicyCheck = ValidateSetVariable( TEST_VAR_1_NAME, > + &mTestGuid1, > + VARIABLE_ATTRIBUTE_BS_RT_AT, > + sizeof(DummyData), > + DummyData ); > + UT_ASSERT_NOT_EFI_ERROR( PolicyCheck ); > + > + // With a policy, make sure that a state variable that's too large doesn't lock > the variable. > + will_return( StubGetVariableNull, TEST_POLICY_ATTRIBUTES_NULL ); // > Attributes > + will_return( StubGetVariableNull, 10 ); // Size > + will_return( StubGetVariableNull, NULL ); // DataPtr > + will_return( StubGetVariableNull, EFI_BUFFER_TOO_SMALL ); // > Status > + PolicyCheck = ValidateSetVariable( TEST_VAR_1_NAME, > + &mTestGuid1, > + VARIABLE_ATTRIBUTE_BS_RT_AT, > + sizeof(DummyData), > + DummyData ); > + UT_ASSERT_NOT_EFI_ERROR( PolicyCheck ); > + > + // With a policy, check a state variable with the wrong value. > + ValidationStateVar = 0; > + will_return( StubGetVariableNull, TEST_POLICY_ATTRIBUTES_NULL ); // > Attributes > + will_return( StubGetVariableNull, sizeof(ValidationStateVar) ); // Size > + will_return( StubGetVariableNull, &ValidationStateVar ); // DataPtr > + will_return( StubGetVariableNull, EFI_SUCCESS ); // Status > + PolicyCheck = ValidateSetVariable( TEST_VAR_1_NAME, > + &mTestGuid1, > + VARIABLE_ATTRIBUTE_BS_RT_AT, > + sizeof(DummyData), > + DummyData ); > + UT_ASSERT_NOT_EFI_ERROR( PolicyCheck ); > + > + // With a policy, check a state variable with another wrong value. > + ValidationStateVar = 10; > + will_return( StubGetVariableNull, TEST_POLICY_ATTRIBUTES_NULL ); // > Attributes > + will_return( StubGetVariableNull, sizeof(ValidationStateVar) ); // Size > + will_return( StubGetVariableNull, &ValidationStateVar ); // DataPtr > + will_return( StubGetVariableNull, EFI_SUCCESS ); // Status > + PolicyCheck = ValidateSetVariable( TEST_VAR_1_NAME, > + &mTestGuid1, > + VARIABLE_ATTRIBUTE_BS_RT_AT, > + sizeof(DummyData), > + DummyData ); > + UT_ASSERT_NOT_EFI_ERROR( PolicyCheck ); > + > + // With a policy, make sure that a call with a correct state variable fails. > + ValidationStateVar = 20; > + will_return( StubGetVariableNull, TEST_POLICY_ATTRIBUTES_NULL ); // > Attributes > + will_return( StubGetVariableNull, sizeof(ValidationStateVar) ); // Size > + will_return( StubGetVariableNull, &ValidationStateVar ); // DataPtr > + will_return( StubGetVariableNull, EFI_SUCCESS ); // Status > + PolicyCheck = ValidateSetVariable( TEST_VAR_1_NAME, > + &mTestGuid1, > + VARIABLE_ATTRIBUTE_BS_RT_AT, > + sizeof(DummyData), > + DummyData ); > + UT_ASSERT_TRUE( EFI_ERROR( PolicyCheck ) ); > + > + return UNIT_TEST_PASSED; > +} > + > +UNIT_TEST_STATUS > +EFIAPI > +LockOnStatePoliciesShouldApplyToNamespaces ( > + IN UNIT_TEST_CONTEXT Context > + ) > +{ > + EXPANDED_VARIABLE_POLICY_ENTRY ValidationPolicy = { > + { > + VARIABLE_POLICY_ENTRY_REVISION, > + 0, // Will be populated by init helper. > + 0, // Will be populated by init helper. > + TEST_GUID_1, > + TEST_POLICY_MIN_SIZE_NULL, > + TEST_POLICY_MAX_SIZE_NULL, > + TEST_POLICY_ATTRIBUTES_NULL, > + TEST_POLICY_ATTRIBUTES_NULL, > + VARIABLE_POLICY_TYPE_LOCK_ON_VAR_STATE > + }, > + { > + TEST_GUID_2, > + 20, // Value > + 0 // Padding > + }, > + L"", > + L"" > + }; > + EFI_STATUS PolicyCheck; > + UINT8 DummyData[12]; > + UINT8 ValidationStateVar; > + UINTN ExpectedDataSize; > + UT_ASSERT_TRUE( InitExpVarPolicyStrings( &ValidationPolicy, NULL, > TEST_VAR_2_NAME ) ); > + > + > + // Without a policy, there should be no constraints on variable creation. > + PolicyCheck = ValidateSetVariable( TEST_VAR_1_NAME, > + &mTestGuid1, > + VARIABLE_ATTRIBUTE_BS_RT_AT, > + sizeof(DummyData), > + DummyData ); > + UT_ASSERT_NOT_EFI_ERROR( PolicyCheck ); > + PolicyCheck = ValidateSetVariable( TEST_VAR_3_NAME, > + &mTestGuid1, > + VARIABLE_ATTRIBUTE_BS_RT_AT, > + sizeof(DummyData), > + DummyData ); > + UT_ASSERT_NOT_EFI_ERROR( PolicyCheck ); > + > + // Set a policy to test against. > + > UT_ASSERT_NOT_EFI_ERROR( RegisterVariablePolicy( &ValidationPolicy.Hea > der ) ); > + > + // Set consistent expectations on what the calls are looking for. > + expect_memory_count( StubGetVariableNull, VariableName, > TEST_VAR_2_NAME, sizeof(TEST_VAR_2_NAME), 4 ); > + expect_memory_count( StubGetVariableNull, VendorGuid, &mTestGuid2, > sizeof(mTestGuid2), 4 ); > + ExpectedDataSize = 1; > + expect_memory_count( StubGetVariableNull, DataSize, > &ExpectedDataSize, sizeof(ExpectedDataSize), 4 ); > + > + // With a policy, make sure that writes still work, since the variable doesn't > exist. > + will_return( StubGetVariableNull, TEST_POLICY_ATTRIBUTES_NULL ); // > Attributes > + will_return( StubGetVariableNull, 0 ); // Size > + will_return( StubGetVariableNull, NULL ); // DataPtr > + will_return( StubGetVariableNull, EFI_NOT_FOUND ); // Status > + PolicyCheck = ValidateSetVariable( TEST_VAR_1_NAME, > + &mTestGuid1, > + VARIABLE_ATTRIBUTE_BS_RT_AT, > + sizeof(DummyData), > + DummyData ); > + UT_ASSERT_NOT_EFI_ERROR( PolicyCheck ); > + will_return( StubGetVariableNull, TEST_POLICY_ATTRIBUTES_NULL ); // > Attributes > + will_return( StubGetVariableNull, 0 ); // Size > + will_return( StubGetVariableNull, NULL ); // DataPtr > + will_return( StubGetVariableNull, EFI_NOT_FOUND ); // Status > + PolicyCheck = ValidateSetVariable( TEST_VAR_3_NAME, > + &mTestGuid1, > + VARIABLE_ATTRIBUTE_BS_RT_AT, > + sizeof(DummyData), > + DummyData ); > + UT_ASSERT_NOT_EFI_ERROR( PolicyCheck ); > + > + // With a policy, make sure that a call with a correct state variable fails. > + ValidationStateVar = 20; > + will_return( StubGetVariableNull, TEST_POLICY_ATTRIBUTES_NULL ); // > Attributes > + will_return( StubGetVariableNull, sizeof(ValidationStateVar) ); // Size > + will_return( StubGetVariableNull, &ValidationStateVar ); // DataPtr > + will_return( StubGetVariableNull, EFI_SUCCESS ); // Status > + PolicyCheck = ValidateSetVariable( TEST_VAR_1_NAME, > + &mTestGuid1, > + VARIABLE_ATTRIBUTE_BS_RT_AT, > + sizeof(DummyData), > + DummyData ); > + UT_ASSERT_TRUE( EFI_ERROR( PolicyCheck ) ); > + will_return( StubGetVariableNull, TEST_POLICY_ATTRIBUTES_NULL ); // > Attributes > + will_return( StubGetVariableNull, sizeof(ValidationStateVar) ); // Size > + will_return( StubGetVariableNull, &ValidationStateVar ); // DataPtr > + will_return( StubGetVariableNull, EFI_SUCCESS ); // Status > + PolicyCheck = ValidateSetVariable( TEST_VAR_3_NAME, > + &mTestGuid1, > + VARIABLE_ATTRIBUTE_BS_RT_AT, > + sizeof(DummyData), > + DummyData ); > + UT_ASSERT_TRUE( EFI_ERROR( PolicyCheck ) ); > + > + return UNIT_TEST_PASSED; > +} > + > +UNIT_TEST_STATUS > +EFIAPI > +LockOnStateShouldHandleErrorsGracefully ( > + IN UNIT_TEST_CONTEXT Context > + ) > +{ > + EXPANDED_VARIABLE_POLICY_ENTRY ValidationPolicy = { > + { > + VARIABLE_POLICY_ENTRY_REVISION, > + 0, // Will be populated by init helper. > + 0, // Will be populated by init helper. > + TEST_GUID_1, > + TEST_POLICY_MIN_SIZE_NULL, > + TEST_POLICY_MAX_SIZE_NULL, > + TEST_POLICY_ATTRIBUTES_NULL, > + TEST_POLICY_ATTRIBUTES_NULL, > + VARIABLE_POLICY_TYPE_LOCK_ON_VAR_STATE > + }, > + { > + TEST_GUID_2, > + 20, // Value > + 0 // Padding > + }, > + L"", > + L"" > + }; > + EFI_STATUS PolicyCheck; > + UINT8 DummyData[12]; > + UT_ASSERT_TRUE( InitExpVarPolicyStrings( &ValidationPolicy, > TEST_VAR_1_NAME, TEST_VAR_2_NAME ) ); > + > + > + // Without a policy, there should be no constraints on variable creation. > + PolicyCheck = ValidateSetVariable( TEST_VAR_1_NAME, > + &mTestGuid1, > + VARIABLE_ATTRIBUTE_BS_RT_AT, > + sizeof(DummyData), > + DummyData ); > + UT_ASSERT_NOT_EFI_ERROR( PolicyCheck ); > + > + // Set a policy to test against. > + > UT_ASSERT_NOT_EFI_ERROR( RegisterVariablePolicy( &ValidationPolicy.Hea > der ) ); > + > + // Configure the stub to not care about parameters. We're testing errors. > + expect_any_always( StubGetVariableNull, VariableName ); > + expect_any_always( StubGetVariableNull, VendorGuid ); > + expect_any_always( StubGetVariableNull, DataSize ); > + > + // With a policy, make sure that writes still work, since the variable doesn't > exist. > + will_return( StubGetVariableNull, TEST_POLICY_ATTRIBUTES_NULL ); // > Attributes > + will_return( StubGetVariableNull, 0 ); // Size > + will_return( StubGetVariableNull, NULL ); // DataPtr > + will_return( StubGetVariableNull, EFI_NOT_FOUND ); // Status > + PolicyCheck = ValidateSetVariable( TEST_VAR_1_NAME, > + &mTestGuid1, > + VARIABLE_ATTRIBUTE_BS_RT_AT, > + sizeof(DummyData), > + DummyData ); > + UT_ASSERT_NOT_EFI_ERROR( PolicyCheck ); > + > + // Verify that state variables that are the wrong size won't lock the variable. > + will_return( StubGetVariableNull, TEST_POLICY_ATTRIBUTES_NULL ); // > Attributes > + will_return( StubGetVariableNull, 0 ); // Size > + will_return( StubGetVariableNull, NULL ); // DataPtr > + will_return( StubGetVariableNull, EFI_BUFFER_TOO_SMALL ); // > Status > + PolicyCheck = ValidateSetVariable( TEST_VAR_1_NAME, > + &mTestGuid1, > + VARIABLE_ATTRIBUTE_BS_RT_AT, > + sizeof(DummyData), > + DummyData ); > + UT_ASSERT_NOT_EFI_ERROR( PolicyCheck ); > + > + // Verify that unexpected errors default to locked. > + will_return( StubGetVariableNull, TEST_POLICY_ATTRIBUTES_NULL ); // > Attributes > + will_return( StubGetVariableNull, 0 ); // Size > + will_return( StubGetVariableNull, NULL ); // DataPtr > + will_return( StubGetVariableNull, EFI_UNSUPPORTED ); // Status > + PolicyCheck = ValidateSetVariable( TEST_VAR_1_NAME, > + &mTestGuid1, > + VARIABLE_ATTRIBUTE_BS_RT_AT, > + sizeof(DummyData), > + DummyData ); > + UT_ASSERT_TRUE( EFI_ERROR( PolicyCheck ) ); > + > + will_return( StubGetVariableNull, TEST_POLICY_ATTRIBUTES_NULL ); // > Attributes > + will_return( StubGetVariableNull, 0 ); // Size > + will_return( StubGetVariableNull, NULL ); // DataPtr > + will_return( StubGetVariableNull, EFI_NOT_READY ); // Status > + PolicyCheck = ValidateSetVariable( TEST_VAR_1_NAME, > + &mTestGuid1, > + VARIABLE_ATTRIBUTE_BS_RT_AT, > + sizeof(DummyData), > + DummyData ); > + UT_ASSERT_TRUE( EFI_ERROR( PolicyCheck ) ); > + > + return UNIT_TEST_PASSED; > +} > + > +UNIT_TEST_STATUS > +EFIAPI > +BestMatchPriorityShouldBeObeyed ( > + IN UNIT_TEST_CONTEXT Context > + ) > +{ > + SIMPLE_VARIABLE_POLICY_ENTRY ValidationPolicy = { > + { > + VARIABLE_POLICY_ENTRY_REVISION, > + sizeof(VARIABLE_POLICY_ENTRY) + sizeof(L"Wild12Card34Placeholder"), > + sizeof(VARIABLE_POLICY_ENTRY), > + TEST_GUID_1, > + TEST_POLICY_MIN_SIZE_NULL, > + TEST_POLICY_MAX_SIZE_NULL, > + TEST_POLICY_ATTRIBUTES_NULL, > + TEST_POLICY_ATTRIBUTES_NULL, > + VARIABLE_POLICY_TYPE_NO_LOCK > + }, > + L"Wild12Card34Placeholder" > + }; > + EFI_STATUS PolicyCheck; > + UINT8 DummyData[70]; > + CHAR16 *PolicyName = (CHAR16*)((UINT8*)&ValidationPolicy + > sizeof(VARIABLE_POLICY_ENTRY)); > + UINTN PolicyNameSize = sizeof(L"Wild12Card34Placeholder"); > + CHAR16 *FourWildcards = L"Wild##Card##Placeholder"; > + CHAR16 *ThreeWildcards = L"Wild##Card#4Placeholder"; > + CHAR16 *TwoWildcards = L"Wild##Card34Placeholder"; > + CHAR16 *OneWildcard = L"Wild#2Card34Placeholder"; > + CHAR16 *NoWildcards = L"Wild12Card34Placeholder"; > + > + // Create all of the policies from least restrictive to most restrictive. > + // NoWildcards should be the most restrictive. > + ValidationPolicy.Header.MaxSize = 60; > + ValidationPolicy.Header.Size = ValidationPolicy.Header.OffsetToName; > + > UT_ASSERT_NOT_EFI_ERROR( RegisterVariablePolicy( &ValidationPolicy.Hea > der ) ); > + ValidationPolicy.Header.Size += (UINT16)PolicyNameSize; > + ValidationPolicy.Header.MaxSize = 50; > + CopyMem( PolicyName, FourWildcards, PolicyNameSize ); > + > UT_ASSERT_NOT_EFI_ERROR( RegisterVariablePolicy( &ValidationPolicy.Hea > der ) ); > + ValidationPolicy.Header.MaxSize = 40; > + CopyMem( PolicyName, ThreeWildcards, PolicyNameSize ); > + > UT_ASSERT_NOT_EFI_ERROR( RegisterVariablePolicy( &ValidationPolicy.Hea > der ) ); > + ValidationPolicy.Header.MaxSize = 30; > + CopyMem( PolicyName, TwoWildcards, PolicyNameSize ); > + > UT_ASSERT_NOT_EFI_ERROR( RegisterVariablePolicy( &ValidationPolicy.Hea > der ) ); > + ValidationPolicy.Header.MaxSize = 20; > + CopyMem( PolicyName, OneWildcard, PolicyNameSize ); > + > UT_ASSERT_NOT_EFI_ERROR( RegisterVariablePolicy( &ValidationPolicy.Hea > der ) ); > + ValidationPolicy.Header.MaxSize = 10; > + CopyMem( PolicyName, NoWildcards, PolicyNameSize ); > + > UT_ASSERT_NOT_EFI_ERROR( RegisterVariablePolicy( &ValidationPolicy.Hea > der ) ); > + > + // Verify that variables only matching the namespace have the most > flexible policy. > + PolicyCheck = ValidateSetVariable( L"ArbitraryName", > + &mTestGuid1, > + VARIABLE_ATTRIBUTE_BS_RT_AT, > + 65, > + DummyData ); > + UT_ASSERT_TRUE( EFI_ERROR( PolicyCheck ) ); > + PolicyCheck = ValidateSetVariable( L"ArbitraryName", > + &mTestGuid1, > + VARIABLE_ATTRIBUTE_BS_RT_AT, > + 55, > + DummyData ); > + UT_ASSERT_NOT_EFI_ERROR( PolicyCheck ); > + > + // Verify that variables matching increasing characters get increasing policy > restrictions. > + PolicyCheck = ValidateSetVariable( L"Wild77Card77Placeholder", > + &mTestGuid1, > + VARIABLE_ATTRIBUTE_BS_RT_AT, > + 55, > + DummyData ); > + UT_ASSERT_TRUE( EFI_ERROR( PolicyCheck ) ); > + PolicyCheck = ValidateSetVariable( L"Wild77Card77Placeholder", > + &mTestGuid1, > + VARIABLE_ATTRIBUTE_BS_RT_AT, > + 45, > + DummyData ); > + UT_ASSERT_NOT_EFI_ERROR( PolicyCheck ); > + > + PolicyCheck = ValidateSetVariable( L"Wild77Card74Placeholder", > + &mTestGuid1, > + VARIABLE_ATTRIBUTE_BS_RT_AT, > + 45, > + DummyData ); > + UT_ASSERT_TRUE( EFI_ERROR( PolicyCheck ) ); > + PolicyCheck = ValidateSetVariable( L"Wild77Card74Placeholder", > + &mTestGuid1, > + VARIABLE_ATTRIBUTE_BS_RT_AT, > + 35, > + DummyData ); > + UT_ASSERT_NOT_EFI_ERROR( PolicyCheck ); > + > + PolicyCheck = ValidateSetVariable( L"Wild77Card34Placeholder", > + &mTestGuid1, > + VARIABLE_ATTRIBUTE_BS_RT_AT, > + 35, > + DummyData ); > + UT_ASSERT_TRUE( EFI_ERROR( PolicyCheck ) ); > + PolicyCheck = ValidateSetVariable( L"Wild77Card34Placeholder", > + &mTestGuid1, > + VARIABLE_ATTRIBUTE_BS_RT_AT, > + 25, > + DummyData ); > + UT_ASSERT_NOT_EFI_ERROR( PolicyCheck ); > + > + PolicyCheck = ValidateSetVariable( L"Wild72Card34Placeholder", > + &mTestGuid1, > + VARIABLE_ATTRIBUTE_BS_RT_AT, > + 25, > + DummyData ); > + UT_ASSERT_TRUE( EFI_ERROR( PolicyCheck ) ); > + PolicyCheck = ValidateSetVariable( L"Wild72Card34Placeholder", > + &mTestGuid1, > + VARIABLE_ATTRIBUTE_BS_RT_AT, > + 15, > + DummyData ); > + UT_ASSERT_NOT_EFI_ERROR( PolicyCheck ); > + > + PolicyCheck = ValidateSetVariable( L"Wild12Card34Placeholder", > + &mTestGuid1, > + VARIABLE_ATTRIBUTE_BS_RT_AT, > + 15, > + DummyData ); > + UT_ASSERT_TRUE( EFI_ERROR( PolicyCheck ) ); > + PolicyCheck = ValidateSetVariable( L"Wild12Card34Placeholder", > + &mTestGuid1, > + VARIABLE_ATTRIBUTE_BS_RT_AT, > + 5, > + DummyData ); > + UT_ASSERT_NOT_EFI_ERROR( PolicyCheck ); > + > + return UNIT_TEST_PASSED; > +} > + > + > +///=== POLICY UTILITY SUITE > =================================================== > + > +UNIT_TEST_STATUS > +EFIAPI > +ShouldBeAbleToLockInterface ( > + IN UNIT_TEST_CONTEXT Context > + ) > +{ > + SIMPLE_VARIABLE_POLICY_ENTRY TestPolicy = { > + { > + VARIABLE_POLICY_ENTRY_REVISION, > + sizeof(VARIABLE_POLICY_ENTRY) + sizeof(TEST_VAR_1_NAME), > + sizeof(VARIABLE_POLICY_ENTRY), > + TEST_GUID_1, > + TEST_POLICY_MIN_SIZE_NULL, > + TEST_POLICY_MAX_SIZE_NULL, > + TEST_POLICY_ATTRIBUTES_NULL, > + TEST_POLICY_ATTRIBUTES_NULL, > + VARIABLE_POLICY_TYPE_NO_LOCK > + }, > + TEST_VAR_1_NAME > + }; > + > + // Make sure it's not already locked. > + UT_ASSERT_FALSE( IsVariablePolicyInterfaceLocked() ); > + // Lock it. > + UT_ASSERT_NOT_EFI_ERROR( LockVariablePolicy() ); > + // Verify that it's locked. > + UT_ASSERT_TRUE( IsVariablePolicyInterfaceLocked() ); > + > + // Verify that all state-changing commands fail. > + UT_ASSERT_TRUE( EFI_ERROR( LockVariablePolicy() ) ); > + UT_ASSERT_TRUE( EFI_ERROR( DisableVariablePolicy() ) ); > + > UT_ASSERT_TRUE( EFI_ERROR( RegisterVariablePolicy( &TestPolicy.Header ) > ) ); > + > + return UNIT_TEST_PASSED; > +} > + > +UNIT_TEST_STATUS > +EFIAPI > +ShouldBeAbleToDisablePolicyEnforcement ( > + IN UNIT_TEST_CONTEXT Context > + ) > +{ > + SIMPLE_VARIABLE_POLICY_ENTRY TestPolicy = { > + { > + VARIABLE_POLICY_ENTRY_REVISION, > + sizeof(VARIABLE_POLICY_ENTRY) + sizeof(TEST_VAR_1_NAME), > + sizeof(VARIABLE_POLICY_ENTRY), > + TEST_GUID_1, > + TEST_POLICY_MIN_SIZE_10, > + TEST_POLICY_MAX_SIZE_200, > + TEST_POLICY_ATTRIBUTES_NULL, > + TEST_POLICY_ATTRIBUTES_NULL, > + VARIABLE_POLICY_TYPE_NO_LOCK > + }, > + TEST_VAR_1_NAME > + }; > + EFI_STATUS PolicyCheck; > + UINT8 DummyData[TEST_POLICY_MIN_SIZE_10-1]; > + > + // Make sure that the policy enforcement is currently enabled. > + UT_ASSERT_TRUE( IsVariablePolicyEnabled() ); > + // Add a policy before it's disabled. > + > UT_ASSERT_NOT_EFI_ERROR( RegisterVariablePolicy( &TestPolicy.Header ) ); > + // Disable the policy enforcement. > + UT_ASSERT_NOT_EFI_ERROR( DisableVariablePolicy() ); > + // Make sure that the policy enforcement is currently disabled. > + UT_ASSERT_FALSE( IsVariablePolicyEnabled() ); > + > + // Check to make sure that a policy violation still passes. > + PolicyCheck = ValidateSetVariable( TEST_VAR_1_NAME, > + &mTestGuid1, > + VARIABLE_ATTRIBUTE_NV_BS, > + sizeof(DummyData), > + DummyData ); > + UT_ASSERT_NOT_EFI_ERROR( PolicyCheck ); > + > + return UNIT_TEST_PASSED; > +} > + > +UNIT_TEST_STATUS > +EFIAPI > +ShouldNotBeAbleToDisablePoliciesTwice ( > + IN UNIT_TEST_CONTEXT Context > + ) > +{ > + // Make sure that the policy enforcement is currently enabled. > + UT_ASSERT_TRUE( IsVariablePolicyEnabled() ); > + // Disable the policy enforcement. > + UT_ASSERT_NOT_EFI_ERROR( DisableVariablePolicy() ); > + // Make sure that the policy enforcement is currently disabled. > + UT_ASSERT_FALSE( IsVariablePolicyEnabled() ); > + // Try to disable again and verify failure. > + UT_ASSERT_TRUE( EFI_ERROR( DisableVariablePolicy() ) ); > + > + return UNIT_TEST_PASSED; > +} > + > +UNIT_TEST_STATUS > +EFIAPI > +ShouldBeAbleToAddNewPoliciesAfterDisabled ( > + IN UNIT_TEST_CONTEXT Context > + ) > +{ > + SIMPLE_VARIABLE_POLICY_ENTRY TestPolicy = { > + { > + VARIABLE_POLICY_ENTRY_REVISION, > + sizeof(VARIABLE_POLICY_ENTRY) + sizeof(TEST_VAR_1_NAME), > + sizeof(VARIABLE_POLICY_ENTRY), > + TEST_GUID_1, > + TEST_POLICY_MIN_SIZE_10, > + TEST_POLICY_MAX_SIZE_200, > + TEST_POLICY_ATTRIBUTES_NULL, > + TEST_POLICY_ATTRIBUTES_NULL, > + VARIABLE_POLICY_TYPE_NO_LOCK > + }, > + TEST_VAR_1_NAME > + }; > + EFI_STATUS PolicyCheck; > + > + // Make sure that the policy enforcement is currently enabled. > + UT_ASSERT_TRUE( IsVariablePolicyEnabled() ); > + // Disable the policy enforcement. > + UT_ASSERT_NOT_EFI_ERROR( DisableVariablePolicy() ); > + > + // Make sure that new policy creation still works, it just won't be enforced. > + PolicyCheck = RegisterVariablePolicy( &TestPolicy.Header ); > + UT_ASSERT_NOT_EFI_ERROR( PolicyCheck ); > + > + return UNIT_TEST_PASSED; > +} > + > +UNIT_TEST_STATUS > +EFIAPI > +ShouldBeAbleToLockAfterDisabled ( > + IN UNIT_TEST_CONTEXT Context > + ) > +{ > + // Make sure that the policy enforcement is currently enabled. > + UT_ASSERT_TRUE( IsVariablePolicyEnabled() ); > + // Disable the policy enforcement. > + UT_ASSERT_NOT_EFI_ERROR( DisableVariablePolicy() ); > + > + // Make sure that we can lock in this state. > + UT_ASSERT_FALSE( IsVariablePolicyInterfaceLocked() ); > + UT_ASSERT_NOT_EFI_ERROR( LockVariablePolicy() ); > + UT_ASSERT_TRUE( IsVariablePolicyInterfaceLocked() ); > + > + return UNIT_TEST_PASSED; > +} > + > +UNIT_TEST_STATUS > +EFIAPI > +ShouldBeAbleToDumpThePolicyTable ( > + IN UNIT_TEST_CONTEXT Context > + ) > +{ > + SIMPLE_VARIABLE_POLICY_ENTRY TestPolicy = { > + { > + VARIABLE_POLICY_ENTRY_REVISION, > + sizeof(VARIABLE_POLICY_ENTRY) + sizeof(TEST_VAR_1_NAME), > + sizeof(VARIABLE_POLICY_ENTRY), > + TEST_GUID_1, > + TEST_POLICY_MIN_SIZE_10, > + TEST_POLICY_MAX_SIZE_200, > + TEST_POLICY_ATTRIBUTES_NULL, > + TEST_POLICY_ATTRIBUTES_NULL, > + VARIABLE_POLICY_TYPE_NO_LOCK > + }, > + TEST_VAR_1_NAME > + }; > + EFI_STATUS PolicyCheck; > + UINT32 DumpSize; > + UINT32 BufferSize; > + VOID *DumpBuffer; > + > + // For good measure, test some parameter validation. > + UT_ASSERT_STATUS_EQUAL( DumpVariablePolicy( NULL, NULL ), > EFI_INVALID_PARAMETER ); > + DumpSize = 10; > + UT_ASSERT_STATUS_EQUAL( DumpVariablePolicy( NULL, &DumpSize ), > EFI_INVALID_PARAMETER ); > + > + // Now for the actual test case. > + > + // Allocate a buffer to hold the output. > + BufferSize = sizeof(VARIABLE_POLICY_ENTRY) + > sizeof(TEST_VAR_1_NAME); > + DumpBuffer = AllocatePool( BufferSize ); > + UT_ASSERT_NOT_EQUAL( DumpBuffer, NULL ); > + > + // Verify that the current table size is 0. > + DumpSize = BufferSize; > + UT_ASSERT_NOT_EFI_ERROR( DumpVariablePolicy( DumpBuffer, > &DumpSize ) ); > + UT_ASSERT_EQUAL( DumpSize, 0 ); > + > + // Now, set a new policy. > + > UT_ASSERT_NOT_EFI_ERROR( RegisterVariablePolicy( &TestPolicy.Header ) ); > + > + // Make sure that the new return is non-zero and fails as expected. > + DumpSize = 0; > + PolicyCheck = DumpVariablePolicy( NULL, &DumpSize ); > + UT_ASSERT_STATUS_EQUAL( PolicyCheck, EFI_BUFFER_TOO_SMALL ); > + UT_ASSERT_EQUAL( DumpSize, BufferSize ); > + > + // Now verify that we can fetch the dump. > + DumpSize = BufferSize; > + UT_ASSERT_NOT_EFI_ERROR( DumpVariablePolicy( DumpBuffer, > &DumpSize ) ); > + UT_ASSERT_EQUAL( DumpSize, BufferSize ); > + UT_ASSERT_MEM_EQUAL( &TestPolicy, DumpBuffer, BufferSize ); > + > + // Always put away your toys. > + FreePool( DumpBuffer ); > + > + return UNIT_TEST_PASSED; > +} > + > +UNIT_TEST_STATUS > +EFIAPI > +ShouldBeAbleToDumpThePolicyTableAfterDisabled ( > + IN UNIT_TEST_CONTEXT Context > + ) > +{ > + SIMPLE_VARIABLE_POLICY_ENTRY TestPolicy = { > + { > + VARIABLE_POLICY_ENTRY_REVISION, > + sizeof(VARIABLE_POLICY_ENTRY) + sizeof(TEST_VAR_1_NAME), > + sizeof(VARIABLE_POLICY_ENTRY), > + TEST_GUID_1, > + TEST_POLICY_MIN_SIZE_10, > + TEST_POLICY_MAX_SIZE_200, > + TEST_POLICY_ATTRIBUTES_NULL, > + TEST_POLICY_ATTRIBUTES_NULL, > + VARIABLE_POLICY_TYPE_NO_LOCK > + }, > + TEST_VAR_1_NAME > + }; > + SIMPLE_VARIABLE_POLICY_ENTRY TestPolicy2 = { > + { > + VARIABLE_POLICY_ENTRY_REVISION, > + sizeof(VARIABLE_POLICY_ENTRY) + sizeof(TEST_VAR_2_NAME), > + sizeof(VARIABLE_POLICY_ENTRY), > + TEST_GUID_2, > + TEST_POLICY_MIN_SIZE_10, > + TEST_POLICY_MAX_SIZE_200, > + TEST_POLICY_ATTRIBUTES_NULL, > + TEST_POLICY_ATTRIBUTES_NULL, > + VARIABLE_POLICY_TYPE_NO_LOCK > + }, > + TEST_VAR_2_NAME > + }; > + EFI_STATUS PolicyCheck; > + UINT32 DumpSize; > + VOID *DumpBuffer; > + > + DumpBuffer = NULL; > + DumpSize = 0; > + > + // Register a new policy. > + > UT_ASSERT_NOT_EFI_ERROR( RegisterVariablePolicy( &TestPolicy.Header ) ); > + // Make sure that we can dump the policy. > + PolicyCheck = DumpVariablePolicy( DumpBuffer, &DumpSize ); > + UT_ASSERT_STATUS_EQUAL( PolicyCheck, EFI_BUFFER_TOO_SMALL ); > + DumpBuffer = AllocatePool( DumpSize ); > + UT_ASSERT_NOT_EFI_ERROR( DumpVariablePolicy( DumpBuffer, > &DumpSize ) ); > + UT_ASSERT_MEM_EQUAL( DumpBuffer, &TestPolicy, DumpSize ); > + > + // Clean up from this step. > + FreePool( DumpBuffer ); > + DumpBuffer = NULL; > + DumpSize = 0; > + > + // Now disable the engine. > + DisableVariablePolicy(); > + > + // Now register a new policy and make sure that both can be dumped. > + > UT_ASSERT_NOT_EFI_ERROR( RegisterVariablePolicy( &TestPolicy2.Header ) > ); > + // Make sure that we can dump the policy. > + PolicyCheck = DumpVariablePolicy( DumpBuffer, &DumpSize ); > + UT_ASSERT_STATUS_EQUAL( PolicyCheck, EFI_BUFFER_TOO_SMALL ); > + DumpBuffer = AllocatePool( DumpSize ); > + UT_ASSERT_NOT_EFI_ERROR( DumpVariablePolicy( DumpBuffer, > &DumpSize ) ); > + > + // Finally, make sure that both policies are in the dump. > + UT_ASSERT_MEM_EQUAL( DumpBuffer, &TestPolicy, > TestPolicy.Header.Size ); > + UT_ASSERT_MEM_EQUAL( (UINT8*)DumpBuffer + TestPolicy.Header.Size, > + &TestPolicy2, > + TestPolicy2.Header.Size ); > + > + // Always put away your toys. > + FreePool( DumpBuffer ); > + > + return UNIT_TEST_PASSED; > +} > + > + > +///=== TEST ENGINE > ========================================================== > ====================== > + > +/** > + SampleUnitTestApp > + > + @param[in] ImageHandle The firmware allocated handle for the EFI image. > + @param[in] SystemTable A pointer to the EFI System Table. > + > + @retval EFI_SUCCESS The entry point executed successfully. > + @retval other Some error occured when executing this entry point. > + > +**/ > +int main () > +{ > + EFI_STATUS Status; > + UNIT_TEST_FRAMEWORK_HANDLE Framework = NULL; > + UNIT_TEST_SUITE_HANDLE ArchTests; > + UNIT_TEST_SUITE_HANDLE PolicyTests; > + UNIT_TEST_SUITE_HANDLE UtilityTests; > +#ifdef INTERNAL_UNIT_TEST > + UNIT_TEST_SUITE_HANDLE InternalTests; > +#endif // INTERNAL_UNIT_TEST > + > + DEBUG(( DEBUG_INFO, "%a v%a\n", UNIT_TEST_NAME, > UNIT_TEST_VERSION )); > + > + // > + // Start setting up the test framework for running the tests. > + // > + Status = InitUnitTestFramework( &Framework, UNIT_TEST_NAME, > gEfiCallerBaseName, UNIT_TEST_VERSION ); > + if (EFI_ERROR( Status )) > + { > + DEBUG((DEBUG_ERROR, "Failed in InitUnitTestFramework. Status = %r\n", > Status)); > + goto EXIT; > + } > + > + > + // > + // Add all test suites and tests. > + // > + Status = CreateUnitTestSuite( &ArchTests, Framework, "Variable Policy > Architectural Tests", "VarPolicy.Arch", NULL, NULL ); > + if (EFI_ERROR( Status )) > + { > + DEBUG((DEBUG_ERROR, "Failed in CreateUnitTestSuite for > ArchTests\n")); > + Status = EFI_OUT_OF_RESOURCES; > + goto EXIT; > + } > + AddTestCase( ArchTests, > + "Deinitialization should fail if not previously initialized", > "VarPolicy.Arch.OnlyDeinit", > + ShouldFailDeinitWithoutInit, NULL, NULL, NULL ); > + AddTestCase( ArchTests, > + "Initialization followed by deinitialization should succeed", > "VarPolicy.Arch.InitDeinit", > + ShouldBeAbleToInitAndDeinitTheLibrary, NULL, NULL, NULL ); > + AddTestCase( ArchTests, > + "The initialization function fail if called twice without a deinit", > "VarPolicy.Arch.InitTwice", > + ShouldNotBeAbleToInitializeTheLibraryTwice, NULL, LibCleanup, > NULL ); > + AddTestCase( ArchTests, > + "API functions should be unavailable until library is initialized", > "VarPolicy.Arch.UninitApiOff", > + ApiCommandsShouldNotRespondIfLibIsUninitialized, NULL, > LibCleanup, NULL ); > + > +#ifdef INTERNAL_UNIT_TEST > + Status = CreateUnitTestSuite( &InternalTests, Framework, "Variable Policy > Internal Tests", "VarPolicy.Internal", NULL, NULL ); > + if (EFI_ERROR( Status )) > + { > + DEBUG((DEBUG_ERROR, "Failed in CreateUnitTestSuite for > InternalTests\n")); > + Status = EFI_OUT_OF_RESOURCES; > + goto EXIT; > + } > + AddTestCase( InternalTests, > + "Policy matching should use name and GUID", > "VarPolicy.Internal.NameGuid", > + PoliciesShouldMatchByNameAndGuid, LibInitMocked, LibCleanup, > NULL ); > + AddTestCase( InternalTests, > + "# sign wildcards should match digits", > "VarPolicy.Internal.WildDigits", > + WildcardPoliciesShouldMatchDigits, LibInitMocked, LibCleanup, > NULL ); > + AddTestCase( InternalTests, > + "Digit wildcards should check edge cases", > "VarPolicy.Internal.WildDigitsAdvanced", > + WildcardPoliciesShouldMatchDigitsAdvanced, LibInitMocked, > LibCleanup, NULL ); > + AddTestCase( InternalTests, > + "Empty names should match an entire namespace", > "VarPolicy.Internal.WildNamespace", > + WildcardPoliciesShouldMatchNamespaces, LibInitMocked, > LibCleanup, NULL ); > + AddTestCase( InternalTests, > + "Match priority should weight correctly based on wildcards", > "VarPolicy.Internal.Priorities", > + MatchPrioritiesShouldFollowRules, LibInitMocked, LibCleanup, > NULL ); > +#endif // INTERNAL_UNIT_TEST > + > + Status = CreateUnitTestSuite( &PolicyTests, Framework, "Variable Policy > Manipulation Tests", "VarPolicy.Policy", NULL, NULL ); > + if (EFI_ERROR( Status )) > + { > + DEBUG((DEBUG_ERROR, "Failed in CreateUnitTestSuite for > PolicyTests\n")); > + Status = EFI_OUT_OF_RESOURCES; > + goto EXIT; > + } > + AddTestCase( PolicyTests, > + "RegisterShouldAllowNamespaceWildcards", > "VarPolicy.Policy.AllowNamespace", > + RegisterShouldAllowNamespaceWildcards, LibInitMocked, > LibCleanup, NULL ); > + AddTestCase( PolicyTests, > + "RegisterShouldAllowStateVarsForNamespaces", > "VarPolicy.Policy.AllowStateNamespace", > + RegisterShouldAllowStateVarsForNamespaces, LibInitMocked, > LibCleanup, NULL ); > + AddTestCase( PolicyTests, > + "RegisterShouldRejectNullPointers", "VarPolicy.Policy.NullPointers", > + RegisterShouldRejectNullPointers, LibInitMocked, LibCleanup, > NULL ); > + AddTestCase( PolicyTests, > + "RegisterShouldRejectBadRevisions", > "VarPolicy.Policy.BadRevisions", > + RegisterShouldRejectBadRevisions, LibInitMocked, LibCleanup, > NULL ); > + AddTestCase( PolicyTests, > + "RegisterShouldRejectBadSizes", "VarPolicy.Policy.BadSizes", > + RegisterShouldRejectBadSizes, LibInitMocked, LibCleanup, NULL ); > + AddTestCase( PolicyTests, > + "RegisterShouldRejectBadOffsets", "VarPolicy.Policy.BadOffsets", > + RegisterShouldRejectBadOffsets, LibInitMocked, LibCleanup, NULL ); > + AddTestCase( PolicyTests, > + "RegisterShouldRejectMissingStateStrings", > "VarPolicy.Policy.MissingStateString", > + RegisterShouldRejectMissingStateStrings, LibInitMocked, > LibCleanup, NULL ); > + AddTestCase( PolicyTests, > + "RegisterShouldRejectStringsMissingNull", > "VarPolicy.Policy.MissingNull", > + RegisterShouldRejectStringsMissingNull, LibInitMocked, LibCleanup, > NULL ); > + AddTestCase( PolicyTests, > + "RegisterShouldRejectMalformedStrings", > "VarPolicy.Policy.MalformedStrings", > + RegisterShouldRejectMalformedStrings, LibInitMocked, LibCleanup, > NULL ); > + AddTestCase( PolicyTests, > + "RegisterShouldRejectUnpackedPolicies", > "VarPolicy.Policy.PolicyPacking", > + RegisterShouldRejectUnpackedPolicies, LibInitMocked, LibCleanup, > NULL ); > + AddTestCase( PolicyTests, > + "RegisterShouldRejectInvalidNameCharacters", > "VarPolicy.Policy.InvalidCharacters", > + RegisterShouldRejectInvalidNameCharacters, LibInitMocked, > LibCleanup, NULL ); > + AddTestCase( PolicyTests, > + "RegisterShouldRejectBadPolicyConstraints", > "VarPolicy.Policy.BadConstraints", > + RegisterShouldRejectBadPolicyConstraints, LibInitMocked, > LibCleanup, NULL ); > + AddTestCase( PolicyTests, > + "RegisterShouldRejectUnknownLockPolicies", > "VarPolicy.Policy.BadLocks", > + RegisterShouldRejectUnknownLockPolicies, LibInitMocked, > LibCleanup, NULL ); > + AddTestCase( PolicyTests, > + "RegisterShouldRejectPolicesWithTooManyWildcards", > "VarPolicy.Policy.TooManyWildcards", > + RegisterShouldRejectPolicesWithTooManyWildcards, LibInitMocked, > LibCleanup, NULL ); > + AddTestCase( PolicyTests, > + "RegisterShouldRejectDuplicatePolicies", > "VarPolicy.Policy.DuplicatePolicies", > + RegisterShouldRejectDuplicatePolicies, LibInitMocked, LibCleanup, > NULL ); > + AddTestCase( PolicyTests, > + "Variables that exceed min or max sizes should be rejected", > "VarPolicy.Policy.MinMax", > + MinAndMaxSizePoliciesShouldBeHonored, LibInitMocked, > LibCleanup, NULL ); > + AddTestCase( PolicyTests, > + "AttributeMustPoliciesShouldBeHonored", > "VarPolicy.Policy.AttrMust", > + AttributeMustPoliciesShouldBeHonored, LibInitMocked, LibCleanup, > NULL ); > + AddTestCase( PolicyTests, > + "AttributeCantPoliciesShouldBeHonored", > "VarPolicy.Policy.AttrCant", > + AttributeCantPoliciesShouldBeHonored, LibInitMocked, LibCleanup, > NULL ); > + AddTestCase( PolicyTests, > + "VariablesShouldBeDeletableRegardlessOfSize", > "VarPolicy.Policy.DeleteIgnoreSize", > + VariablesShouldBeDeletableRegardlessOfSize, LibInitMocked, > LibCleanup, NULL ); > + AddTestCase( PolicyTests, > + "LockNowPoliciesShouldBeHonored", > "VarPolicy.Policy.VARIABLE_POLICY_TYPE_LOCK_NOW", > + LockNowPoliciesShouldBeHonored, LibInitMocked, LibCleanup, > NULL ); > + AddTestCase( PolicyTests, > + "LockOnCreatePoliciesShouldBeHonored", > "VarPolicy.Policy.VARIABLE_POLICY_TYPE_LOCK_ON_CREATE", > + LockOnCreatePoliciesShouldBeHonored, LibInitMocked, LibCleanup, > NULL ); > + AddTestCase( PolicyTests, > + "LockOnStatePoliciesShouldBeHonored", > "VarPolicy.Policy.LockState", > + LockOnStatePoliciesShouldBeHonored, LibInitMocked, LibCleanup, > NULL ); > + AddTestCase( PolicyTests, > + "LockOnStatePoliciesShouldApplyToNamespaces", > "VarPolicy.Policy.NamespaceLockState", > + LockOnStatePoliciesShouldApplyToNamespaces, LibInitMocked, > LibCleanup, NULL ); > + AddTestCase( PolicyTests, > + "LockOnStateShouldHandleErrorsGracefully", > "VarPolicy.Policy.LockStateErrors", > + LockOnStateShouldHandleErrorsGracefully, LibInitMocked, > LibCleanup, NULL ); > + AddTestCase( PolicyTests, > + "BestMatchPriorityShouldBeObeyed", "VarPolicy.Policy.BestMatch", > + BestMatchPriorityShouldBeObeyed, LibInitMocked, LibCleanup, > NULL ); > + > + Status = CreateUnitTestSuite( &UtilityTests, Framework, "Variable Policy > Utility Tests", "VarPolicy.Utility", NULL, NULL ); > + if (EFI_ERROR( Status )) > + { > + DEBUG((DEBUG_ERROR, "Failed in CreateUnitTestSuite for > UtilityTests\n")); > + Status = EFI_OUT_OF_RESOURCES; > + goto EXIT; > + } > + AddTestCase( UtilityTests, > + "API commands that change state should not respond after > interface is locked", "VarPolicy.Utility.InterfaceLock", > + ShouldBeAbleToLockInterface, LibInitMocked, LibCleanup, NULL ); > + AddTestCase( UtilityTests, > + "All policies should pass once enforcement is disabled", > "VarPolicy.Utility.DisableEnforcement", > + ShouldBeAbleToDisablePolicyEnforcement, LibInitMocked, > LibCleanup, NULL ); > + AddTestCase( UtilityTests, > + "Disabling enforcement twice should produce an error", > "VarPolicy.Utility.DisableEnforcementTwice", > + ShouldNotBeAbleToDisablePoliciesTwice, LibInitMocked, > LibCleanup, NULL ); > + AddTestCase( UtilityTests, > + "ShouldBeAbleToAddNewPoliciesAfterDisabled", > "VarPolicy.Utility.AddAfterDisable", > + ShouldBeAbleToAddNewPoliciesAfterDisabled, LibInitMocked, > LibCleanup, NULL ); > + AddTestCase( UtilityTests, > + "ShouldBeAbleToLockAfterDisabled", > "VarPolicy.Utility.LockAfterDisable", > + ShouldBeAbleToLockAfterDisabled, LibInitMocked, LibCleanup, > NULL ); > + AddTestCase( UtilityTests, > + "Should be able to dump the policy table", > "VarPolicy.Utility.DumpTable", > + ShouldBeAbleToDumpThePolicyTable, LibInitMocked, LibCleanup, > NULL ); > + AddTestCase( UtilityTests, > + "ShouldBeAbleToDumpThePolicyTableAfterDisabled", > "VarPolicy.Utility.DumpTableAfterDisable", > + ShouldBeAbleToDumpThePolicyTableAfterDisabled, LibInitMocked, > LibCleanup, NULL ); > + > + > + // > + // Execute the tests. > + // > + Status = RunAllTestSuites( Framework ); > + > +EXIT: > + if (Framework) > + { > + FreeUnitTestFramework( Framework ); > + } > + > + return Status; > +} > diff --git a/MdeModulePkg/Include/Library/VariablePolicyLib.h > b/MdeModulePkg/Include/Library/VariablePolicyLib.h > new file mode 100644 > index 000000000000..6be16fdd8f24 > --- /dev/null > +++ b/MdeModulePkg/Include/Library/VariablePolicyLib.h > @@ -0,0 +1,206 @@ > +/** @file -- VariablePolicyLib.h > +Business logic for Variable Policy enforcement. > + > +Copyright (c) Microsoft Corporation. > +SPDX-License-Identifier: BSD-2-Clause-Patent > + > +**/ > + > +#ifndef _VARIABLE_POLICY_LIB_H_ > +#define _VARIABLE_POLICY_LIB_H_ > + > +#include <Protocol/VariablePolicy.h> > + > +/** > + This API function validates and registers a new policy with > + the policy enforcement engine. > + > + @param[in] NewPolicy Pointer to the incoming policy structure. > + > + @retval EFI_SUCCESS > + @retval EFI_INVALID_PARAMETER NewPolicy is NULL or is internally > inconsistent. > + @retval EFI_ALREADY_STARTED An identical matching policy already > exists. > + @retval EFI_WRITE_PROTECTED The interface has been locked until > the next reboot. > + @retval EFI_UNSUPPORTED Policy enforcement has been disabled. > No reason to add more policies. > + @retval EFI_ABORTED A calculation error has prevented this > function from completing. > + @retval EFI_OUT_OF_RESOURCES Cannot grow the table to hold any > more policies. > + @retval EFI_NOT_READY Library has not yet been initialized. > + > +**/ > +EFI_STATUS > +EFIAPI > +RegisterVariablePolicy ( > + IN CONST VARIABLE_POLICY_ENTRY *NewPolicy > + ); > + > + > +/** > + This API function checks to see whether the parameters to SetVariable > would > + be allowed according to the current variable policies. > + > + @param[in] VariableName Same as EFI_SET_VARIABLE. > + @param[in] VendorGuid Same as EFI_SET_VARIABLE. > + @param[in] Attributes Same as EFI_SET_VARIABLE. > + @param[in] DataSize Same as EFI_SET_VARIABLE. > + @param[in] Data Same as EFI_SET_VARIABLE. > + > + @retval EFI_SUCCESS A matching policy allows this update. > + @retval EFI_SUCCESS There are currently no policies that restrict > this update. > + @retval EFI_SUCCESS The protections have been disable until the > next reboot. > + @retval EFI_WRITE_PROTECTED Variable is currently locked. > + @retval EFI_INVALID_PARAMETER Attributes or size are invalid. > + @retval EFI_ABORTED A lock policy exists, but an error prevented > evaluation. > + @retval EFI_NOT_READY Library has not been initialized. > + > +**/ > +EFI_STATUS > +EFIAPI > +ValidateSetVariable ( > + IN CHAR16 *VariableName, > + IN EFI_GUID *VendorGuid, > + IN UINT32 Attributes, > + IN UINTN DataSize, > + IN VOID *Data > + ); > + > + > +/** > + This API function disables the variable policy enforcement. If it's > + already been called once, will return EFI_ALREADY_STARTED. > + > + @retval EFI_SUCCESS > + @retval EFI_ALREADY_STARTED Has already been called once this boot. > + @retval EFI_WRITE_PROTECTED Interface has been locked until reboot. > + @retval EFI_NOT_READY Library has not yet been initialized. > + > +**/ > +EFI_STATUS > +EFIAPI > +DisableVariablePolicy ( > + VOID > + ); > + > + > +/** > + This API function will dump the entire contents of the variable policy table. > + > + Similar to GetVariable, the first call can be made with a 0 size and it will > return > + the size of the buffer required to hold the entire table. > + > + @param[out] Policy Pointer to the policy buffer. Can be NULL if Size is 0. > + @param[in,out] Size On input, the size of the output buffer. On output, > the size > + of the data returned. > + > + @retval EFI_SUCCESS Policy data is in the output buffer and Size > has been updated. > + @retval EFI_INVALID_PARAMETER Size is NULL, or Size is non-zero and > Policy is NULL. > + @retval EFI_BUFFER_TOO_SMALL Size is insufficient to hold policy. Size > updated with required size. > + @retval EFI_NOT_READY Library has not yet been initialized. > + > +**/ > +EFI_STATUS > +EFIAPI > +DumpVariablePolicy ( > + OUT UINT8 *Policy, > + IN OUT UINT32 *Size > + ); > + > + > +/** > + This API function returns whether or not the policy engine is > + currently being enforced. > + > + @retval TRUE > + @retval FALSE > + @retval FALSE Library has not yet been initialized. > + > +**/ > +BOOLEAN > +EFIAPI > +IsVariablePolicyEnabled ( > + VOID > + ); > + > + > +/** > + This API function locks the interface so that no more policy updates > + can be performed or changes made to the enforcement until the next > boot. > + > + @retval EFI_SUCCESS > + @retval EFI_NOT_READY Library has not yet been initialized. > + > +**/ > +EFI_STATUS > +EFIAPI > +LockVariablePolicy ( > + VOID > + ); > + > + > +/** > + This API function returns whether or not the policy interface is locked > + for the remainder of the boot. > + > + @retval TRUE > + @retval FALSE > + @retval FALSE Library has not yet been initialized. > + > +**/ > +BOOLEAN > +EFIAPI > +IsVariablePolicyInterfaceLocked ( > + VOID > + ); > + > + > +/** > + This helper function initializes the library and sets > + up any required internal structures or handlers. > + > + Also registers the internal pointer for the GetVariable helper. > + > + @param[in] GetVariableHelper A function pointer matching the > EFI_GET_VARIABLE prototype that will be used to > + check policy criteria that involve the existence of other variables. > + > + @retval EFI_SUCCESS > + @retval EFI_ALREADY_STARTED The initialize function has been called > more than once without a call to > + deinitialize. > + > +**/ > +EFI_STATUS > +EFIAPI > +InitVariablePolicyLib ( > + IN EFI_GET_VARIABLE GetVariableHelper > + ); > + > + > +/** > + This helper function returns whether or not the library is currently > initialized. > + > + @retval TRUE > + @retval FALSE > + > +**/ > +BOOLEAN > +EFIAPI > +IsVariablePolicyLibInitialized ( > + VOID > + ); > + > + > +/** > + This helper function tears down the library. > + > + Should generally only be used for test harnesses. > + > + @retval EFI_SUCCESS > + @retval EFI_NOT_READY Deinitialize was called without first calling > initialize. > + > +**/ > +EFI_STATUS > +EFIAPI > +DeinitVariablePolicyLib ( > + VOID > + ); > + > + > +#endif // _VARIABLE_POLICY_LIB_H_ > diff --git a/MdeModulePkg/Library/VariablePolicyLib/VariablePolicyLib.inf > b/MdeModulePkg/Library/VariablePolicyLib/VariablePolicyLib.inf > new file mode 100644 > index 000000000000..340d5e8793fe > --- /dev/null > +++ b/MdeModulePkg/Library/VariablePolicyLib/VariablePolicyLib.inf > @@ -0,0 +1,38 @@ > +## @file VariablePolicyLib.inf > +# Business logic for Variable Policy enforcement. > +# > +## > +# Copyright (c) Microsoft Corporation. > +# SPDX-License-Identifier: BSD-2-Clause-Patent > +## > + > + > +[Defines] > + INF_VERSION = 0x00010017 > + BASE_NAME = VariablePolicyLib > + FILE_GUID = E9ECD342-159A-4F24-9FDF-65724027C594 > + VERSION_STRING = 1.0 > + MODULE_TYPE = BASE > + LIBRARY_CLASS = VariablePolicyLib > + > +# > +# The following information is for reference only and not required by the > build tools. > +# > +# VALID_ARCHITECTURES = ANY > +# > + > + > +[Sources] > + VariablePolicyLib.c > + > + > +[Packages] > + MdePkg/MdePkg.dec > + MdeModulePkg/MdeModulePkg.dec > + > + > +[LibraryClasses] > + DebugLib > + BaseMemoryLib > + MemoryAllocationLib > + SafeIntLib > diff --git a/MdeModulePkg/Library/VariablePolicyLib/VariablePolicyLib.uni > b/MdeModulePkg/Library/VariablePolicyLib/VariablePolicyLib.uni > new file mode 100644 > index 000000000000..2227ec427828 > --- /dev/null > +++ b/MdeModulePkg/Library/VariablePolicyLib/VariablePolicyLib.uni > @@ -0,0 +1,12 @@ > +// /** @file > +// VariablePolicyLib.uni > +// > +// Copyright (c) Microsoft Corporation. > +// SPDX-License-Identifier: BSD-2-Clause-Patent > +// > +// **/ > + > + > +#string STR_MODULE_ABSTRACT #language en-US "Library containing > the business logic for the VariablePolicy engine" > + > +#string STR_MODULE_DESCRIPTION #language en-US "Library > containing the business logic for the VariablePolicy engine" > diff --git > a/MdeModulePkg/Library/VariablePolicyLib/VariablePolicyUnitTest/Variable > PolicyUnitTest.inf > b/MdeModulePkg/Library/VariablePolicyLib/VariablePolicyUnitTest/Variable > PolicyUnitTest.inf > new file mode 100644 > index 000000000000..c7c636eabde3 > --- /dev/null > +++ > b/MdeModulePkg/Library/VariablePolicyLib/VariablePolicyUnitTest/Variable > PolicyUnitTest.inf > @@ -0,0 +1,41 @@ > +## @file VariablePolicyUnitTest.inf > +# UnitTest for... > +# Business logic for Variable Policy enforcement. > +# > +## > +# Copyright (c) Microsoft Corporation. > +# SPDX-License-Identifier: BSD-2-Clause-Patent > +## > + > + > +[Defines] > + INF_VERSION = 0x00010006 > + BASE_NAME = VariablePolicyUnitTest > + FILE_GUID = 1200A2E4-D756-418C-9768-528C2D181A98 > + MODULE_TYPE = HOST_APPLICATION > + VERSION_STRING = 1.0 > + > +# > +# The following information is for reference only and not required by the > build tools. > +# > +# VALID_ARCHITECTURES = IA32 X64 ARM AARCH64 > +# > + > +[Sources] > + VariablePolicyUnitTest.c > + > + > +[Packages] > + MdePkg/MdePkg.dec > + MdeModulePkg/MdeModulePkg.dec > + UnitTestFrameworkPkg/UnitTestFrameworkPkg.dec > + > + > +[LibraryClasses] > + BaseLib > + DebugLib > + UnitTestLib > + PrintLib > + VariablePolicyLib > + BaseMemoryLib > + MemoryAllocationLib > diff --git a/MdeModulePkg/MdeModulePkg.dec > b/MdeModulePkg/MdeModulePkg.dec > index 956276e30a72..990e23b07a08 100644 > --- a/MdeModulePkg/MdeModulePkg.dec > +++ b/MdeModulePkg/MdeModulePkg.dec > @@ -29,6 +29,9 @@ > ## @libraryclass Defines a set of methods to reset whole system. > ResetSystemLib|Include/Library/ResetSystemLib.h > > + ## @libraryclass Business logic for storing and testing variable policies > + VariablePolicyLib|Include/Library/VariablePolicyLib.h > + > ## @libraryclass Defines a set of helper functions for resetting the system. > ResetUtilityLib|Include/Library/ResetUtilityLib.h > > diff --git a/MdeModulePkg/MdeModulePkg.dsc > b/MdeModulePkg/MdeModulePkg.dsc > index f7dbb27ce25d..8501dae88eb1 100644 > --- a/MdeModulePkg/MdeModulePkg.dsc > +++ b/MdeModulePkg/MdeModulePkg.dsc > @@ -3,6 +3,7 @@ > # > # (C) Copyright 2014 Hewlett-Packard Development Company, L.P.<BR> > # Copyright (c) 2007 - 2019, Intel Corporation. All rights reserved.<BR> > +# Copyright (c) Microsoft Corporation. > # > # SPDX-License-Identifier: BSD-2-Clause-Patent > # > @@ -58,6 +59,7 @@ > DxeServicesLib|MdePkg/Library/DxeServicesLib/DxeServicesLib.inf > > DxeServicesTableLib|MdePkg/Library/DxeServicesTableLib/DxeServicesTabl > eLib.inf > > UefiBootManagerLib|MdeModulePkg/Library/UefiBootManagerLib/UefiBoo > tManagerLib.inf > + > VariablePolicyLib|MdeModulePkg/Library/VariablePolicyLib/VariablePolicyLi > b.inf > # > # Generic Modules > # > @@ -306,6 +308,7 @@ > MdeModulePkg/Library/BootLogoLib/BootLogoLib.inf > > MdeModulePkg/Library/TpmMeasurementLibNull/TpmMeasurementLibNull > .inf > MdeModulePkg/Library/AuthVariableLibNull/AuthVariableLibNull.inf > + MdeModulePkg/Library/VariablePolicyLib/VariablePolicyLib.inf > MdeModulePkg/Library/VarCheckLib/VarCheckLib.inf > MdeModulePkg/Library/VarCheckHiiLib/VarCheckHiiLib.inf > MdeModulePkg/Library/VarCheckPcdLib/VarCheckPcdLib.inf > diff --git a/MdeModulePkg/Test/MdeModulePkgHostTest.dsc > b/MdeModulePkg/Test/MdeModulePkgHostTest.dsc > index 72a119db4568..058ef7dcef11 100644 > --- a/MdeModulePkg/Test/MdeModulePkgHostTest.dsc > +++ b/MdeModulePkg/Test/MdeModulePkgHostTest.dsc > @@ -19,12 +19,20 @@ > > !include UnitTestFrameworkPkg/UnitTestFrameworkPkgHost.dsc.inc > > +[LibraryClasses] > + SafeIntLib|MdePkg/Library/BaseSafeIntLib/BaseSafeIntLib.inf > + > [Components] > > MdeModulePkg/Library/DxeResetSystemLib/UnitTest/MockUefiRuntimeSer > vicesTableLib.inf > > # > # Build MdeModulePkg HOST_APPLICATION Tests > # > + > MdeModulePkg/Library/VariablePolicyLib/VariablePolicyUnitTest/VariablePo > licyUnitTest.inf { > + <LibraryClasses> > + > VariablePolicyLib|MdeModulePkg/Library/VariablePolicyLib/VariablePolicyLi > b.inf > + } > + > > MdeModulePkg/Library/DxeResetSystemLib/UnitTest/DxeResetSystemLibU > nitTestHost.inf { > <LibraryClasses> > > ResetSystemLib|MdeModulePkg/Library/DxeResetSystemLib/DxeResetSyst > emLib.inf > -- > 2.16.3.windows.1 > > > ^ permalink raw reply related [flat|nested] 10+ messages in thread
* [PATCH v1 3/9] MdeModulePkg: Define the VariablePolicyHelperLib [not found] <20200410183802.21192-1-michael.kubacki@outlook.com> 2020-04-10 18:37 ` [PATCH v1 2/9] MdeModulePkg: Define the VariablePolicyLib Michael Kubacki @ 2020-04-10 18:37 ` Michael Kubacki 2020-04-26 2:03 ` [edk2-devel] " Guomin Jiang 2020-04-10 18:37 ` [PATCH v1 4/9] MdeModulePkg: Define the VarCheckPolicyLib and SMM interface Michael Kubacki ` (5 subsequent siblings) 7 siblings, 1 reply; 10+ messages in thread From: Michael Kubacki @ 2020-04-10 18:37 UTC (permalink / raw) To: devel; +Cc: Jian J Wang, Hao A Wu, Liming Gao From: Bret Barkelew <brbarkel@microsoft.com> https://bugzilla.tianocore.org/show_bug.cgi?id=2522 VariablePolicy is an updated interface to replace VarLock and VarCheckProtocol. Add the VariablePolicyHelperLib library, containing several functions to help with the repetitive process of creating a correctly structured and packed VariablePolicy entry. Cc: Jian J Wang <jian.j.wang@intel.com> Cc: Hao A Wu <hao.a.wu@intel.com> Cc: Liming Gao <liming.gao@intel.com> Signed-off-by: Bret Barkelew <brbarkel@microsoft.com> Signed-off-by: Michael Kubacki <michael.kubacki@microsoft.com> --- MdeModulePkg/Library/VariablePolicyHelperLib/VariablePolicyHelperLib.c | 396 ++++++++++++++++++++ MdeModulePkg/Include/Library/VariablePolicyHelperLib.h | 164 ++++++++ MdeModulePkg/Library/VariablePolicyHelperLib/VariablePolicyHelperLib.inf | 36 ++ MdeModulePkg/Library/VariablePolicyHelperLib/VariablePolicyHelperLib.uni | 12 + MdeModulePkg/MdeModulePkg.dec | 5 + MdeModulePkg/MdeModulePkg.dsc | 2 + 6 files changed, 615 insertions(+) diff --git a/MdeModulePkg/Library/VariablePolicyHelperLib/VariablePolicyHelperLib.c b/MdeModulePkg/Library/VariablePolicyHelperLib/VariablePolicyHelperLib.c new file mode 100644 index 000000000000..7cf58b6cb31c --- /dev/null +++ b/MdeModulePkg/Library/VariablePolicyHelperLib/VariablePolicyHelperLib.c @@ -0,0 +1,396 @@ +/** @file -- VariablePolicyHelperLib.c +This library contains helper functions for marshalling and registering +new policies with the VariablePolicy infrastructure. + +This library is currently written against VariablePolicy revision 0x00010000. + +Copyright (c) Microsoft Corporation. +SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include <Uefi.h> + +#include <Library/BaseLib.h> +#include <Library/DebugLib.h> +#include <Library/BaseMemoryLib.h> +#include <Library/MemoryAllocationLib.h> + +#include <Protocol/VariablePolicy.h> + +/** + This internal helper function populates the header structure, + all common fields, and takes care of fix-ups. + + NOTE: Only use this internally. Assumes correctly-sized buffers. + + @param[out] EntPtr Pointer to the buffer to be populated. + @param[in] Namespace Pointer to an EFI_GUID for the target variable namespace that this policy will protect. + @param[in] MinSize MinSize for the VariablePolicy. + @param[in] MaxSize MaxSize for the VariablePolicy. + @param[in] AttributesMustHave AttributesMustHave for the VariablePolicy. + @param[in] AttributesCantHave AttributesCantHave for the VariablePolicy. + @param[in] LockPolicyType LockPolicyType for the VariablePolicy. + +**/ +STATIC +VOID +PopulateCommonData ( + OUT VARIABLE_POLICY_ENTRY *EntPtr, + IN CONST EFI_GUID *Namespace, + IN UINT32 MinSize, + IN UINT32 MaxSize, + IN UINT32 AttributesMustHave, + IN UINT32 AttributesCantHave, + IN UINT8 LockPolicyType + ) +{ + EntPtr->Version = VARIABLE_POLICY_ENTRY_REVISION; + CopyGuid( &EntPtr->Namespace, Namespace ); + EntPtr->MinSize = MinSize; + EntPtr->MaxSize = MaxSize; + EntPtr->AttributesMustHave = AttributesMustHave; + EntPtr->AttributesCantHave = AttributesCantHave; + EntPtr->LockPolicyType = LockPolicyType; + + // NOTE: As a heler, fix up MaxSize for compatibility with the old model. + if (EntPtr->MaxSize == 0) { + EntPtr->MaxSize = VARIABLE_POLICY_NO_MAX_SIZE; + } + + return; +} + + +/** + This helper function will allocate and populate a new VariablePolicy + structure for a policy that does not contain any sub-structures (such as + VARIABLE_LOCK_ON_VAR_STATE_POLICY). + + NOTE: Caller will need to free structure once finished. + + @param[in] Namespace Pointer to an EFI_GUID for the target variable namespace that this policy will protect. + @param[in] Name [Optional] If provided, a pointer to the CHAR16 array for the target variable name. + Otherwise, will create a policy that targets an entire namespace. + @param[in] MinSize MinSize for the VariablePolicy. + @param[in] MaxSize MaxSize for the VariablePolicy. + @param[in] AttributesMustHave AttributesMustHave for the VariablePolicy. + @param[in] AttributesCantHave AttributesCantHave for the VariablePolicy. + @param[in] LockPolicyType LockPolicyType for the VariablePolicy. + @param[out] NewEntry If successful, will be set to a pointer to the allocated buffer containing the + new policy. + + @retval EFI_SUCCESS Operation completed successfully and structure is populated. + @retval EFI_INVALID_PARAMETER Namespace is NULL. + @retval EFI_INVALID_PARAMETER LockPolicyType is invalid for a basic structure. + @retval EFI_BUFFER_TOO_SMALL Finished structure would not fit in UINT16 size. + @retval EFI_OUT_OF_RESOURCES Could not allocate sufficient space for structure. + +**/ +EFI_STATUS +EFIAPI +CreateBasicVariablePolicy ( + IN CONST EFI_GUID *Namespace, + IN CONST CHAR16 *Name OPTIONAL, + IN UINT32 MinSize, + IN UINT32 MaxSize, + IN UINT32 AttributesMustHave, + IN UINT32 AttributesCantHave, + IN UINT8 LockPolicyType, + OUT VARIABLE_POLICY_ENTRY **NewEntry + ) +{ + UINTN TotalSize; + UINTN NameSize; + VARIABLE_POLICY_ENTRY *EntPtr; + CHAR16 *CopyName; + + // Check some initial invalid parameters for this function. + if (Namespace == NULL || NewEntry == NULL) { + return EFI_INVALID_PARAMETER; + } + if (LockPolicyType != VARIABLE_POLICY_TYPE_NO_LOCK && + LockPolicyType != VARIABLE_POLICY_TYPE_LOCK_NOW && + LockPolicyType != VARIABLE_POLICY_TYPE_LOCK_ON_CREATE) { + return EFI_INVALID_PARAMETER; + } + + // Now we've gotta determine the total size of the buffer required for + // the VariablePolicy structure. + TotalSize = sizeof( VARIABLE_POLICY_ENTRY ); + if (Name != NULL) { + NameSize = StrnSizeS( Name, MAX_UINT16 ); + TotalSize += NameSize; + } + // Make sure the size fits within a VARIABLE_POLICY_ENTRY.Size. + ASSERT( TotalSize <= MAX_UINT16 ); + if (TotalSize > MAX_UINT16) { + return EFI_BUFFER_TOO_SMALL; + } + + // Allocate a buffer to hold all the data. We're on the home stretch. + *NewEntry = AllocatePool( TotalSize ); + if (*NewEntry == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + // If we're still here, we're basically done. + // Copy the data and GET... OUT.... + EntPtr = *NewEntry; + PopulateCommonData ( EntPtr, + Namespace, + MinSize, + MaxSize, + AttributesMustHave, + AttributesCantHave, + LockPolicyType ); + EntPtr->Size = (UINT16)TotalSize; // This is safe because we've already checked. + EntPtr->OffsetToName = sizeof(VARIABLE_POLICY_ENTRY); + if (Name != NULL) { + CopyName = (CHAR16*)((UINT8*)EntPtr + EntPtr->OffsetToName); + CopyMem( CopyName, Name, NameSize ); + } + + return EFI_SUCCESS; +} + + +/** + This helper function will allocate and populate a new VariablePolicy + structure for a policy with a lock type of VARIABLE_POLICY_TYPE_LOCK_ON_VAR_STATE. + + NOTE: Caller will need to free structure once finished. + + @param[in] Namespace Pointer to an EFI_GUID for the target variable namespace that this policy will protect. + @param[in] Name [Optional] If provided, a pointer to the CHAR16 array for the target variable name. + Otherwise, will create a policy that targets an entire namespace. + @param[in] MinSize MinSize for the VariablePolicy. + @param[in] MaxSize MaxSize for the VariablePolicy. + @param[in] AttributesMustHave AttributesMustHave for the VariablePolicy. + @param[in] AttributesCantHave AttributesCantHave for the VariablePolicy. + @param[in] VarStateNamespace Pointer to the EFI_GUID for the VARIABLE_LOCK_ON_VAR_STATE_POLICY.Namespace. + @param[in] VarStateValue Value for the VARIABLE_LOCK_ON_VAR_STATE_POLICY.Value. + @param[in] VarStateName Pointer to the CHAR16 array for the VARIABLE_LOCK_ON_VAR_STATE_POLICY.Name. + @param[out] NewEntry If successful, will be set to a pointer to the allocated buffer containing the + new policy. + + @retval EFI_SUCCESS Operation completed successfully and structure is populated. + @retval EFI_INVALID_PARAMETER Namespace, VarStateNamespace, VarStateName is NULL. + @retval EFI_BUFFER_TOO_SMALL Finished structure would not fit in UINT16 size. + @retval EFI_OUT_OF_RESOURCES Could not allocate sufficient space for structure. + +**/ +EFI_STATUS +EFIAPI +CreateVarStateVariablePolicy ( + IN CONST EFI_GUID *Namespace, + IN CONST CHAR16 *Name OPTIONAL, + IN UINT32 MinSize, + IN UINT32 MaxSize, + IN UINT32 AttributesMustHave, + IN UINT32 AttributesCantHave, + IN CONST EFI_GUID *VarStateNamespace, + IN UINT8 VarStateValue, + IN CONST CHAR16 *VarStateName, + OUT VARIABLE_POLICY_ENTRY **NewEntry + ) +{ + UINTN TotalSize; + UINTN NameSize; + UINTN VarStateNameSize; + VARIABLE_POLICY_ENTRY *EntPtr; + CHAR16 *CopyName; + VARIABLE_LOCK_ON_VAR_STATE_POLICY *CopyPolicy; + + // Check some initial invalid parameters for this function. + if (Namespace == NULL || VarStateNamespace == NULL || + VarStateName == NULL || NewEntry == NULL) { + return EFI_INVALID_PARAMETER; + } + + // Now we've gotta determine the total size of the buffer required for + // the VariablePolicy structure. + VarStateNameSize = StrnSizeS( VarStateName, MAX_UINT16 ); + TotalSize = sizeof( VARIABLE_POLICY_ENTRY ) + + sizeof(VARIABLE_LOCK_ON_VAR_STATE_POLICY) + + VarStateNameSize; + if (Name != NULL) { + NameSize = StrnSizeS( Name, MAX_UINT16 ); + TotalSize += NameSize; + } + // Make sure the size fits within a VARIABLE_POLICY_ENTRY.Size. + ASSERT( TotalSize <= MAX_UINT16 ); + if (TotalSize > MAX_UINT16) { + return EFI_BUFFER_TOO_SMALL; + } + + // Allocate a buffer to hold all the data. We're on the home stretch. + *NewEntry = AllocatePool( TotalSize ); + if (*NewEntry == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + // If we're still here, we're basically done. + // Copy the data and GET... OUT.... + EntPtr = *NewEntry; + PopulateCommonData ( EntPtr, + Namespace, + MinSize, + MaxSize, + AttributesMustHave, + AttributesCantHave, + VARIABLE_POLICY_TYPE_LOCK_ON_VAR_STATE ); + EntPtr->Size = (UINT16)TotalSize; // This is safe because we've already checked. + EntPtr->OffsetToName = sizeof(VARIABLE_POLICY_ENTRY) + + sizeof(VARIABLE_LOCK_ON_VAR_STATE_POLICY) + + (UINT16)VarStateNameSize; + + CopyPolicy = (VARIABLE_LOCK_ON_VAR_STATE_POLICY*)((UINT8*)EntPtr + sizeof(VARIABLE_POLICY_ENTRY)); + CopyName = (CHAR16*)((UINT8*)CopyPolicy + sizeof(VARIABLE_LOCK_ON_VAR_STATE_POLICY)); + CopyGuid( &CopyPolicy->Namespace, VarStateNamespace ); + CopyPolicy->Value = VarStateValue; + CopyMem( CopyName, VarStateName, VarStateNameSize ); + + if (Name != NULL) { + CopyName = (CHAR16*)((UINT8*)EntPtr + EntPtr->OffsetToName); + CopyMem( CopyName, Name, NameSize ); + } + + return EFI_SUCCESS; +} + + +/** + This helper function does everything that CreateBasicVariablePolicy() does, but also + uses the passed in protocol to register the policy with the infrastructure. + Does not return a buffer, does not require the caller to free anything. + + @param[in] VariablePolicy Pointer to a valid instance of the VariablePolicy protocol. + @param[in] Namespace Pointer to an EFI_GUID for the target variable namespace that this policy will protect. + @param[in] Name [Optional] If provided, a pointer to the CHAR16 array for the target variable name. + Otherwise, will create a policy that targets an entire namespace. + @param[in] MinSize MinSize for the VariablePolicy. + @param[in] MaxSize MaxSize for the VariablePolicy. + @param[in] AttributesMustHave AttributesMustHave for the VariablePolicy. + @param[in] AttributesCantHave AttributesCantHave for the VariablePolicy. + @param[in] LockPolicyType LockPolicyType for the VariablePolicy. + + @retval EFI_INVALID_PARAMETER VariablePolicy pointer is NULL. + @retval EFI_STATUS Status returned by CreateBasicVariablePolicy() or RegisterVariablePolicy(). + +**/ +EFI_STATUS +EFIAPI +RegisterBasicVariablePolicy ( + IN VARIABLE_POLICY_PROTOCOL *VariablePolicy, + IN CONST EFI_GUID *Namespace, + IN CONST CHAR16 *Name OPTIONAL, + IN UINT32 MinSize, + IN UINT32 MaxSize, + IN UINT32 AttributesMustHave, + IN UINT32 AttributesCantHave, + IN UINT8 LockPolicyType + ) +{ + VARIABLE_POLICY_ENTRY *NewEntry; + EFI_STATUS Status; + + // Check the simple things. + if (VariablePolicy == NULL) { + return EFI_INVALID_PARAMETER; + } + + // Create the new entry and make sure that everything worked. + NewEntry = NULL; + Status = CreateBasicVariablePolicy( Namespace, + Name, + MinSize, + MaxSize, + AttributesMustHave, + AttributesCantHave, + LockPolicyType, + &NewEntry ); + + // If that was successful, attempt to register the new policy. + if (!EFI_ERROR( Status )) { + Status = VariablePolicy->RegisterVariablePolicy( NewEntry ); + } + + // If we allocated the buffer, free the buffer. + if (NewEntry != NULL) { + FreePool( NewEntry ); + } + + return Status; +} + + +/** + This helper function does everything that CreateBasicVariablePolicy() does, but also + uses the passed in protocol to register the policy with the infrastructure. + Does not return a buffer, does not require the caller to free anything. + + @param[in] VariablePolicy Pointer to a valid instance of the VariablePolicy protocol. + @param[in] Namespace Pointer to an EFI_GUID for the target variable namespace that this policy will protect. + @param[in] Name [Optional] If provided, a pointer to the CHAR16 array for the target variable name. + Otherwise, will create a policy that targets an entire namespace. + @param[in] MinSize MinSize for the VariablePolicy. + @param[in] MaxSize MaxSize for the VariablePolicy. + @param[in] AttributesMustHave AttributesMustHave for the VariablePolicy. + @param[in] AttributesCantHave AttributesCantHave for the VariablePolicy. + @param[in] VarStateNamespace Pointer to the EFI_GUID for the VARIABLE_LOCK_ON_VAR_STATE_POLICY.Namespace. + @param[in] VarStateName Pointer to the CHAR16 array for the VARIABLE_LOCK_ON_VAR_STATE_POLICY.Name. + @param[in] VarStateValue Value for the VARIABLE_LOCK_ON_VAR_STATE_POLICY.Value. + + @retval EFI_INVALID_PARAMETER VariablePolicy pointer is NULL. + @retval EFI_STATUS Status returned by CreateBasicVariablePolicy() or RegisterVariablePolicy(). + +**/ +EFI_STATUS +EFIAPI +RegisterVarStateVariablePolicy ( + IN VARIABLE_POLICY_PROTOCOL *VariablePolicy, + IN CONST EFI_GUID *Namespace, + IN CONST CHAR16 *Name OPTIONAL, + IN UINT32 MinSize, + IN UINT32 MaxSize, + IN UINT32 AttributesMustHave, + IN UINT32 AttributesCantHave, + IN CONST EFI_GUID *VarStateNamespace, + IN CONST CHAR16 *VarStateName, + IN UINT8 VarStateValue + ) +{ + VARIABLE_POLICY_ENTRY *NewEntry; + EFI_STATUS Status; + + // Check the simple things. + if (VariablePolicy == NULL) { + return EFI_INVALID_PARAMETER; + } + + // Create the new entry and make sure that everything worked. + NewEntry = NULL; + Status = CreateVarStateVariablePolicy( Namespace, + Name, + MinSize, + MaxSize, + AttributesMustHave, + AttributesCantHave, + VarStateNamespace, + VarStateValue, + VarStateName, + &NewEntry ); + + // If that was successful, attempt to register the new policy. + if (!EFI_ERROR( Status )) { + Status = VariablePolicy->RegisterVariablePolicy( NewEntry ); + } + + // If we allocated the buffer, free the buffer. + if (NewEntry != NULL) { + FreePool( NewEntry ); + } + + return Status; +} diff --git a/MdeModulePkg/Include/Library/VariablePolicyHelperLib.h b/MdeModulePkg/Include/Library/VariablePolicyHelperLib.h new file mode 100644 index 000000000000..721a55931aab --- /dev/null +++ b/MdeModulePkg/Include/Library/VariablePolicyHelperLib.h @@ -0,0 +1,164 @@ +/** @file -- VariablePolicyHelperLib.h +This library contains helper functions for marshalling and registering +new policies with the VariablePolicy infrastructure. + +Copyright (c) Microsoft Corporation. +SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef _VARIABLE_POLICY_HELPER_LIB_H_ +#define _VARIABLE_POLICY_HELPER_LIB_H_ + +#include <Protocol/VariablePolicy.h> + +/** + This helper function will allocate and populate a new VariablePolicy + structure for a policy that does not contain any sub-structures (such as + VARIABLE_LOCK_ON_VAR_STATE_POLICY). + + NOTE: Caller will need to free structure once finished. + + @param[in] Namespace Pointer to an EFI_GUID for the target variable namespace that this policy will protect. + @param[in] Name [Optional] If provided, a pointer to the CHAR16 array for the target variable name. + Otherwise, will create a policy that targets an entire namespace. + @param[in] MinSize MinSize for the VariablePolicy. + @param[in] MaxSize MaxSize for the VariablePolicy. + @param[in] AttributesMustHave AttributesMustHave for the VariablePolicy. + @param[in] AttributesCantHave AttributesCantHave for the VariablePolicy. + @param[in] LockPolicyType LockPolicyType for the VariablePolicy. + @param[out] NewEntry If successful, will be set to a pointer to the allocated buffer containing the + new policy. + + @retval EFI_SUCCESS Operation completed successfully and structure is populated. + @retval EFI_INVALID_PARAMETER Namespace is NULL. + @retval EFI_INVALID_PARAMETER LockPolicyType is invalid for a basic structure. + @retval EFI_BUFFER_TOO_SMALL Finished structure would not fit in UINT16 size. + @retval EFI_OUT_OF_RESOURCES Could not allocate sufficient space for structure. + +**/ +EFI_STATUS +EFIAPI +CreateBasicVariablePolicy ( + IN CONST EFI_GUID *Namespace, + IN CONST CHAR16 *Name OPTIONAL, + IN UINT32 MinSize, + IN UINT32 MaxSize, + IN UINT32 AttributesMustHave, + IN UINT32 AttributesCantHave, + IN UINT8 LockPolicyType, + OUT VARIABLE_POLICY_ENTRY **NewEntry + ); + + +/** + This helper function will allocate and populate a new VariablePolicy + structure for a policy with a lock type of VARIABLE_POLICY_TYPE_LOCK_ON_VAR_STATE. + + NOTE: Caller will need to free structure once finished. + + @param[in] Namespace Pointer to an EFI_GUID for the target variable namespace that this policy will protect. + @param[in] Name [Optional] If provided, a pointer to the CHAR16 array for the target variable name. + Otherwise, will create a policy that targets an entire namespace. + @param[in] MinSize MinSize for the VariablePolicy. + @param[in] MaxSize MaxSize for the VariablePolicy. + @param[in] AttributesMustHave AttributesMustHave for the VariablePolicy. + @param[in] AttributesCantHave AttributesCantHave for the VariablePolicy. + @param[in] VarStateNamespace Pointer to the EFI_GUID for the VARIABLE_LOCK_ON_VAR_STATE_POLICY.Namespace. + @param[in] VarStateValue Value for the VARIABLE_LOCK_ON_VAR_STATE_POLICY.Value. + @param[in] VarStateName Pointer to the CHAR16 array for the VARIABLE_LOCK_ON_VAR_STATE_POLICY.Name. + @param[out] NewEntry If successful, will be set to a pointer to the allocated buffer containing the + new policy. + + @retval EFI_SUCCESS Operation completed successfully and structure is populated. + @retval EFI_INVALID_PARAMETER Namespace, VarStateNamespace, VarStateName is NULL. + @retval EFI_BUFFER_TOO_SMALL Finished structure would not fit in UINT16 size. + @retval EFI_OUT_OF_RESOURCES Could not allocate sufficient space for structure. + +**/ +EFI_STATUS +EFIAPI +CreateVarStateVariablePolicy ( + IN CONST EFI_GUID *Namespace, + IN CONST CHAR16 *Name OPTIONAL, + IN UINT32 MinSize, + IN UINT32 MaxSize, + IN UINT32 AttributesMustHave, + IN UINT32 AttributesCantHave, + IN CONST EFI_GUID *VarStateNamespace, + IN UINT8 VarStateValue, + IN CONST CHAR16 *VarStateName, + OUT VARIABLE_POLICY_ENTRY **NewEntry + ); + + +/** + This helper function does everything that CreateBasicVariablePolicy() does, but also + uses the passed in protocol to register the policy with the infrastructure. + Does not return a buffer, does not require the caller to free anything. + + @param[in] VariablePolicy Pointer to a valid instance of the VariablePolicy protocol. + @param[in] Namespace Pointer to an EFI_GUID for the target variable namespace that this policy will protect. + @param[in] Name [Optional] If provided, a pointer to the CHAR16 array for the target variable name. + Otherwise, will create a policy that targets an entire namespace. + @param[in] MinSize MinSize for the VariablePolicy. + @param[in] MaxSize MaxSize for the VariablePolicy. + @param[in] AttributesMustHave AttributesMustHave for the VariablePolicy. + @param[in] AttributesCantHave AttributesCantHave for the VariablePolicy. + @param[in] LockPolicyType LockPolicyType for the VariablePolicy. + + @retval EFI_INVALID_PARAMETER VariablePolicy pointer is NULL. + @retval EFI_STATUS Status returned by CreateBasicVariablePolicy() or RegisterVariablePolicy(). + +**/ +EFI_STATUS +EFIAPI +RegisterBasicVariablePolicy ( + IN VARIABLE_POLICY_PROTOCOL *VariablePolicy, + IN CONST EFI_GUID *Namespace, + IN CONST CHAR16 *Name OPTIONAL, + IN UINT32 MinSize, + IN UINT32 MaxSize, + IN UINT32 AttributesMustHave, + IN UINT32 AttributesCantHave, + IN UINT8 LockPolicyType + ); + + +/** + This helper function does everything that CreateBasicVariablePolicy() does, but also + uses the passed in protocol to register the policy with the infrastructure. + Does not return a buffer, does not require the caller to free anything. + + @param[in] VariablePolicy Pointer to a valid instance of the VariablePolicy protocol. + @param[in] Namespace Pointer to an EFI_GUID for the target variable namespace that this policy will protect. + @param[in] Name [Optional] If provided, a pointer to the CHAR16 array for the target variable name. + Otherwise, will create a policy that targets an entire namespace. + @param[in] MinSize MinSize for the VariablePolicy. + @param[in] MaxSize MaxSize for the VariablePolicy. + @param[in] AttributesMustHave AttributesMustHave for the VariablePolicy. + @param[in] AttributesCantHave AttributesCantHave for the VariablePolicy. + @param[in] VarStateNamespace Pointer to the EFI_GUID for the VARIABLE_LOCK_ON_VAR_STATE_POLICY.Namespace. + @param[in] VarStateName Pointer to the CHAR16 array for the VARIABLE_LOCK_ON_VAR_STATE_POLICY.Name. + @param[in] VarStateValue Value for the VARIABLE_LOCK_ON_VAR_STATE_POLICY.Value. + + @retval EFI_INVALID_PARAMETER VariablePolicy pointer is NULL. + @retval EFI_STATUS Status returned by CreateBasicVariablePolicy() or RegisterVariablePolicy(). + +**/ +EFI_STATUS +EFIAPI +RegisterVarStateVariablePolicy ( + IN VARIABLE_POLICY_PROTOCOL *VariablePolicy, + IN CONST EFI_GUID *Namespace, + IN CONST CHAR16 *Name OPTIONAL, + IN UINT32 MinSize, + IN UINT32 MaxSize, + IN UINT32 AttributesMustHave, + IN UINT32 AttributesCantHave, + IN CONST EFI_GUID *VarStateNamespace, + IN CONST CHAR16 *VarStateName, + IN UINT8 VarStateValue + ); + +#endif // _VARIABLE_POLICY_HELPER_LIB_H_ diff --git a/MdeModulePkg/Library/VariablePolicyHelperLib/VariablePolicyHelperLib.inf b/MdeModulePkg/Library/VariablePolicyHelperLib/VariablePolicyHelperLib.inf new file mode 100644 index 000000000000..551435dce8d3 --- /dev/null +++ b/MdeModulePkg/Library/VariablePolicyHelperLib/VariablePolicyHelperLib.inf @@ -0,0 +1,36 @@ +## @file VariablePolicyHelperLib.inf +# This library contains helper functions for marshalling and registering +# new policies with the VariablePolicy infrastructure. +# +# This library is currently written against VariablePolicy revision 0x00010000. +# +## +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: BSD-2-Clause-Patent +## + + +[Defines] + INF_VERSION = 0x00010017 + BASE_NAME = VariablePolicyHelperLib + # MODULE_UNI_FILE = VariablePolicyHelperLib.uni + FILE_GUID = B3C2206B-FDD1-4AED-8352-FC5EC34C5630 + VERSION_STRING = 1.0 + MODULE_TYPE = BASE + LIBRARY_CLASS = VariablePolicyHelperLib + + +[Sources] + VariablePolicyHelperLib.c + + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + + +[LibraryClasses] + BaseLib + DebugLib + MemoryAllocationLib + BaseMemoryLib diff --git a/MdeModulePkg/Library/VariablePolicyHelperLib/VariablePolicyHelperLib.uni b/MdeModulePkg/Library/VariablePolicyHelperLib/VariablePolicyHelperLib.uni new file mode 100644 index 000000000000..39cbf11a4ce9 --- /dev/null +++ b/MdeModulePkg/Library/VariablePolicyHelperLib/VariablePolicyHelperLib.uni @@ -0,0 +1,12 @@ +// /** @file +// VariablePolicyHelperLib.uni +// +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: BSD-2-Clause-Patent +// +// **/ + + +#string STR_MODULE_ABSTRACT #language en-US "Library containing helper functions for marshalling and registering new policies with the VariablePolicy infrastructure" + +#string STR_MODULE_DESCRIPTION #language en-US "Library containing helper functions for marshalling and registering new policies with the VariablePolicy infrastructure" diff --git a/MdeModulePkg/MdeModulePkg.dec b/MdeModulePkg/MdeModulePkg.dec index 990e23b07a08..8fd0a6a4fdfb 100644 --- a/MdeModulePkg/MdeModulePkg.dec +++ b/MdeModulePkg/MdeModulePkg.dec @@ -147,6 +147,11 @@ # DisplayUpdateProgressLib|Include/Library/DisplayUpdateProgressLib.h + ## @libraryclass This library contains helper functions for marshalling and + # registering new policies with the VariablePolicy infrastructure. + # + VariablePolicyHelperLib|Include/Library/VariablePolicyHelperLib.h + [Guids] ## MdeModule package token space guid # Include/Guid/MdeModulePkgTokenSpace.h diff --git a/MdeModulePkg/MdeModulePkg.dsc b/MdeModulePkg/MdeModulePkg.dsc index 8501dae88eb1..c27a3b488a32 100644 --- a/MdeModulePkg/MdeModulePkg.dsc +++ b/MdeModulePkg/MdeModulePkg.dsc @@ -99,6 +99,7 @@ BmpSupportLib|MdeModulePkg/Library/BaseBmpSupportLib/BaseBmpSupportLib.inf SafeIntLib|MdePkg/Library/BaseSafeIntLib/BaseSafeIntLib.inf DisplayUpdateProgressLib|MdeModulePkg/Library/DisplayUpdateProgressLibGraphics/DisplayUpdateProgressLibGraphics.inf + VariablePolicyHelperLib|MdeModulePkg/Library/VariablePolicyHelperLib/VariablePolicyHelperLib.inf [LibraryClasses.EBC.PEIM] IoLib|MdePkg/Library/PeiIoLibCpuIo/PeiIoLibCpuIo.inf @@ -224,6 +225,7 @@ MdeModulePkg/Library/UefiHiiServicesLib/UefiHiiServicesLib.inf MdeModulePkg/Library/BaseHobLibNull/BaseHobLibNull.inf MdeModulePkg/Library/BaseMemoryAllocationLibNull/BaseMemoryAllocationLibNull.inf + MdeModulePkg/Library/VariablePolicyHelperLib/VariablePolicyHelperLib.inf MdeModulePkg/Bus/Pci/PciHostBridgeDxe/PciHostBridgeDxe.inf MdeModulePkg/Bus/Pci/PciSioSerialDxe/PciSioSerialDxe.inf -- 2.16.3.windows.1 ^ permalink raw reply related [flat|nested] 10+ messages in thread
* Re: [edk2-devel] [PATCH v1 3/9] MdeModulePkg: Define the VariablePolicyHelperLib 2020-04-10 18:37 ` [PATCH v1 3/9] MdeModulePkg: Define the VariablePolicyHelperLib Michael Kubacki @ 2020-04-26 2:03 ` Guomin Jiang 0 siblings, 0 replies; 10+ messages in thread From: Guomin Jiang @ 2020-04-26 2:03 UTC (permalink / raw) To: devel@edk2.groups.io, michael.kubacki@outlook.com Cc: Wang, Jian J, Wu, Hao A, Gao, Liming Add comment inline. > -----Original Message----- > From: devel@edk2.groups.io <devel@edk2.groups.io> On Behalf Of Michael > Kubacki > Sent: Saturday, April 11, 2020 2:38 AM > To: devel@edk2.groups.io > Cc: Wang, Jian J <jian.j.wang@intel.com>; Wu, Hao A <hao.a.wu@intel.com>; > Gao, Liming <liming.gao@intel.com> > Subject: [edk2-devel] [PATCH v1 3/9] MdeModulePkg: Define the > VariablePolicyHelperLib > > From: Bret Barkelew <brbarkel@microsoft.com> > > https://bugzilla.tianocore.org/show_bug.cgi?id=2522 > > VariablePolicy is an updated interface to replace VarLock and > VarCheckProtocol. > > Add the VariablePolicyHelperLib library, containing several functions to help > with the repetitive process of creating a correctly structured and packed > VariablePolicy entry. > > Cc: Jian J Wang <jian.j.wang@intel.com> > Cc: Hao A Wu <hao.a.wu@intel.com> > Cc: Liming Gao <liming.gao@intel.com> > Signed-off-by: Bret Barkelew <brbarkel@microsoft.com> > Signed-off-by: Michael Kubacki <michael.kubacki@microsoft.com> > --- > MdeModulePkg/Library/VariablePolicyHelperLib/VariablePolicyHelperLib.c > | 396 ++++++++++++++++++++ > MdeModulePkg/Include/Library/VariablePolicyHelperLib.h | 164 > ++++++++ > MdeModulePkg/Library/VariablePolicyHelperLib/VariablePolicyHelperLib.inf > | 36 ++ > MdeModulePkg/Library/VariablePolicyHelperLib/VariablePolicyHelperLib.uni > | 12 + > MdeModulePkg/MdeModulePkg.dec | 5 + > MdeModulePkg/MdeModulePkg.dsc | 2 + > 6 files changed, 615 insertions(+) > > diff --git > a/MdeModulePkg/Library/VariablePolicyHelperLib/VariablePolicyHelperLib.c > b/MdeModulePkg/Library/VariablePolicyHelperLib/VariablePolicyHelperLib.c > new file mode 100644 > index 000000000000..7cf58b6cb31c > --- /dev/null > +++ > b/MdeModulePkg/Library/VariablePolicyHelperLib/VariablePolicyHelperL > +++ ib.c > @@ -0,0 +1,396 @@ > +/** @file -- VariablePolicyHelperLib.c > +This library contains helper functions for marshalling and registering > +new policies with the VariablePolicy infrastructure. > + > +This library is currently written against VariablePolicy revision 0x00010000. > + > +Copyright (c) Microsoft Corporation. > +SPDX-License-Identifier: BSD-2-Clause-Patent > + > +**/ > + > +#include <Uefi.h> > + > +#include <Library/BaseLib.h> > +#include <Library/DebugLib.h> > +#include <Library/BaseMemoryLib.h> > +#include <Library/MemoryAllocationLib.h> > + > +#include <Protocol/VariablePolicy.h> > + > +/** > + This internal helper function populates the header structure, > + all common fields, and takes care of fix-ups. > + > + NOTE: Only use this internally. Assumes correctly-sized buffers. > + > + @param[out] EntPtr Pointer to the buffer to be populated. > + @param[in] Namespace Pointer to an EFI_GUID for the target variable > namespace that this policy will protect. > + @param[in] MinSize MinSize for the VariablePolicy. > + @param[in] MaxSize MaxSize for the VariablePolicy. > + @param[in] AttributesMustHave AttributesMustHave for the > VariablePolicy. > + @param[in] AttributesCantHave AttributesCantHave for the > VariablePolicy. > + @param[in] LockPolicyType LockPolicyType for the VariablePolicy. > + > +**/ > +STATIC > +VOID > +PopulateCommonData ( > + OUT VARIABLE_POLICY_ENTRY *EntPtr, > + IN CONST EFI_GUID *Namespace, > + IN UINT32 MinSize, > + IN UINT32 MaxSize, > + IN UINT32 AttributesMustHave, > + IN UINT32 AttributesCantHave, > + IN UINT8 LockPolicyType > + ) > +{ > + EntPtr->Version = VARIABLE_POLICY_ENTRY_REVISION; > + CopyGuid( &EntPtr->Namespace, Namespace ); > + EntPtr->MinSize = MinSize; > + EntPtr->MaxSize = MaxSize; > + EntPtr->AttributesMustHave = AttributesMustHave; > + EntPtr->AttributesCantHave = AttributesCantHave; > + EntPtr->LockPolicyType = LockPolicyType; > + > + // NOTE: As a heler, fix up MaxSize for compatibility with the old model. > + if (EntPtr->MaxSize == 0) { > + EntPtr->MaxSize = VARIABLE_POLICY_NO_MAX_SIZE; } > + > + return; > +} > + > + > +/** > + This helper function will allocate and populate a new VariablePolicy > + structure for a policy that does not contain any sub-structures (such > +as > + VARIABLE_LOCK_ON_VAR_STATE_POLICY). > + > + NOTE: Caller will need to free structure once finished. > + > + @param[in] Namespace Pointer to an EFI_GUID for the target variable > namespace that this policy will protect. > + @param[in] Name [Optional] If provided, a pointer to the CHAR16 > array for the target variable name. > + Otherwise, will create a policy that targets an entire > namespace. > + @param[in] MinSize MinSize for the VariablePolicy. > + @param[in] MaxSize MaxSize for the VariablePolicy. > + @param[in] AttributesMustHave AttributesMustHave for the > VariablePolicy. > + @param[in] AttributesCantHave AttributesCantHave for the > VariablePolicy. > + @param[in] LockPolicyType LockPolicyType for the VariablePolicy. > + @param[out] NewEntry If successful, will be set to a pointer to the > allocated buffer containing the > + new policy. > + > + @retval EFI_SUCCESS Operation completed successfully and > structure is populated. > + @retval EFI_INVALID_PARAMETER Namespace is NULL. > + @retval EFI_INVALID_PARAMETER LockPolicyType is invalid for a basic > structure. > + @retval EFI_BUFFER_TOO_SMALL Finished structure would not fit in > UINT16 size. > + @retval EFI_OUT_OF_RESOURCES Could not allocate sufficient space > for structure. > + > +**/ > +EFI_STATUS > +EFIAPI > +CreateBasicVariablePolicy ( > + IN CONST EFI_GUID *Namespace, > + IN CONST CHAR16 *Name OPTIONAL, > + IN UINT32 MinSize, > + IN UINT32 MaxSize, > + IN UINT32 AttributesMustHave, > + IN UINT32 AttributesCantHave, > + IN UINT8 LockPolicyType, > + OUT VARIABLE_POLICY_ENTRY **NewEntry > + ) > +{ > + UINTN TotalSize; > + UINTN NameSize; > + VARIABLE_POLICY_ENTRY *EntPtr; > + CHAR16 *CopyName; > + > + // Check some initial invalid parameters for this function. > + if (Namespace == NULL || NewEntry == NULL) { > + return EFI_INVALID_PARAMETER; > + } > + if (LockPolicyType != VARIABLE_POLICY_TYPE_NO_LOCK && > + LockPolicyType != VARIABLE_POLICY_TYPE_LOCK_NOW && > + LockPolicyType != VARIABLE_POLICY_TYPE_LOCK_ON_CREATE) { > + return EFI_INVALID_PARAMETER; > + } > + > + // Now we've gotta determine the total size of the buffer required > + for // the VariablePolicy structure. > + TotalSize = sizeof( VARIABLE_POLICY_ENTRY ); if (Name != NULL) { > + NameSize = StrnSizeS( Name, MAX_UINT16 ); > + TotalSize += NameSize; > + } > + // Make sure the size fits within a VARIABLE_POLICY_ENTRY.Size. > + ASSERT( TotalSize <= MAX_UINT16 ); > + if (TotalSize > MAX_UINT16) { > + return EFI_BUFFER_TOO_SMALL; > + } > + > + // Allocate a buffer to hold all the data. We're on the home stretch. > + *NewEntry = AllocatePool( TotalSize ); if (*NewEntry == NULL) { > + return EFI_OUT_OF_RESOURCES; > + } > + > + // If we're still here, we're basically done. > + // Copy the data and GET... OUT.... > + EntPtr = *NewEntry; > + PopulateCommonData ( EntPtr, > + Namespace, > + MinSize, > + MaxSize, > + AttributesMustHave, > + AttributesCantHave, > + LockPolicyType ); > + EntPtr->Size = (UINT16)TotalSize; // This is safe because we've > already checked. > + EntPtr->OffsetToName = sizeof(VARIABLE_POLICY_ENTRY); > + if (Name != NULL) { > + CopyName = (CHAR16*)((UINT8*)EntPtr + EntPtr->OffsetToName); > + CopyMem( CopyName, Name, NameSize ); } > + > + return EFI_SUCCESS; > +} > + > + > +/** > + This helper function will allocate and populate a new VariablePolicy > + structure for a policy with a lock type of > VARIABLE_POLICY_TYPE_LOCK_ON_VAR_STATE. > + > + NOTE: Caller will need to free structure once finished. > + > + @param[in] Namespace Pointer to an EFI_GUID for the target variable > namespace that this policy will protect. > + @param[in] Name [Optional] If provided, a pointer to the CHAR16 > array for the target variable name. > + Otherwise, will create a policy that targets an entire > namespace. > + @param[in] MinSize MinSize for the VariablePolicy. > + @param[in] MaxSize MaxSize for the VariablePolicy. > + @param[in] AttributesMustHave AttributesMustHave for the > VariablePolicy. > + @param[in] AttributesCantHave AttributesCantHave for the > VariablePolicy. > + @param[in] VarStateNamespace Pointer to the EFI_GUID for the > VARIABLE_LOCK_ON_VAR_STATE_POLICY.Namespace. > + @param[in] VarStateValue Value for the > VARIABLE_LOCK_ON_VAR_STATE_POLICY.Value. > + @param[in] VarStateName Pointer to the CHAR16 array for the > VARIABLE_LOCK_ON_VAR_STATE_POLICY.Name. > + @param[out] NewEntry If successful, will be set to a pointer to the > allocated buffer containing the > + new policy. > + > + @retval EFI_SUCCESS Operation completed successfully and > structure is populated. > + @retval EFI_INVALID_PARAMETER Namespace, VarStateNamespace, > VarStateName is NULL. > + @retval EFI_BUFFER_TOO_SMALL Finished structure would not fit in > UINT16 size. > + @retval EFI_OUT_OF_RESOURCES Could not allocate sufficient space > for structure. > + > +**/ > +EFI_STATUS > +EFIAPI > +CreateVarStateVariablePolicy ( > + IN CONST EFI_GUID *Namespace, > + IN CONST CHAR16 *Name OPTIONAL, > + IN UINT32 MinSize, > + IN UINT32 MaxSize, > + IN UINT32 AttributesMustHave, > + IN UINT32 AttributesCantHave, > + IN CONST EFI_GUID *VarStateNamespace, > + IN UINT8 VarStateValue, > + IN CONST CHAR16 *VarStateName, > + OUT VARIABLE_POLICY_ENTRY **NewEntry > + ) > +{ > + UINTN TotalSize; > + UINTN NameSize; > + UINTN VarStateNameSize; > + VARIABLE_POLICY_ENTRY *EntPtr; > + CHAR16 *CopyName; > + VARIABLE_LOCK_ON_VAR_STATE_POLICY *CopyPolicy; > + > + // Check some initial invalid parameters for this function. > + if (Namespace == NULL || VarStateNamespace == NULL || > + VarStateName == NULL || NewEntry == NULL) { > + return EFI_INVALID_PARAMETER; > + } > + > + // Now we've gotta determine the total size of the buffer required > + for // the VariablePolicy structure. > + VarStateNameSize = StrnSizeS( VarStateName, MAX_UINT16 ); TotalSize > + = sizeof( VARIABLE_POLICY_ENTRY ) + > + sizeof(VARIABLE_LOCK_ON_VAR_STATE_POLICY) + > + VarStateNameSize; > + if (Name != NULL) { > + NameSize = StrnSizeS( Name, MAX_UINT16 ); > + TotalSize += NameSize; > + } > + // Make sure the size fits within a VARIABLE_POLICY_ENTRY.Size. > + ASSERT( TotalSize <= MAX_UINT16 ); > + if (TotalSize > MAX_UINT16) { > + return EFI_BUFFER_TOO_SMALL; > + } > + > + // Allocate a buffer to hold all the data. We're on the home stretch. > + *NewEntry = AllocatePool( TotalSize ); if (*NewEntry == NULL) { > + return EFI_OUT_OF_RESOURCES; > + } > + > + // If we're still here, we're basically done. > + // Copy the data and GET... OUT.... > + EntPtr = *NewEntry; > + PopulateCommonData ( EntPtr, > + Namespace, > + MinSize, > + MaxSize, > + AttributesMustHave, > + AttributesCantHave, > + VARIABLE_POLICY_TYPE_LOCK_ON_VAR_STATE ); > + EntPtr->Size = (UINT16)TotalSize; // This is safe because we've > already checked. > + EntPtr->OffsetToName = sizeof(VARIABLE_POLICY_ENTRY) + > + sizeof(VARIABLE_LOCK_ON_VAR_STATE_POLICY) + > + (UINT16)VarStateNameSize; > + > + CopyPolicy = (VARIABLE_LOCK_ON_VAR_STATE_POLICY*)((UINT8*)EntPtr > + > + sizeof(VARIABLE_POLICY_ENTRY)); CopyName = > + (CHAR16*)((UINT8*)CopyPolicy + > + sizeof(VARIABLE_LOCK_ON_VAR_STATE_POLICY)); > + CopyGuid( &CopyPolicy->Namespace, VarStateNamespace ); > + CopyPolicy->Value = VarStateValue; CopyMem( CopyName, > VarStateName, > + VarStateNameSize ); > + > + if (Name != NULL) { > + CopyName = (CHAR16*)((UINT8*)EntPtr + EntPtr->OffsetToName); > + CopyMem( CopyName, Name, NameSize ); } > + > + return EFI_SUCCESS; > +} > + > + > +/** > + This helper function does everything that CreateBasicVariablePolicy() > +does, but also > + uses the passed in protocol to register the policy with the infrastructure. > + Does not return a buffer, does not require the caller to free anything. > + > + @param[in] VariablePolicy Pointer to a valid instance of the VariablePolicy > protocol. > + @param[in] Namespace Pointer to an EFI_GUID for the target variable > namespace that this policy will protect. > + @param[in] Name [Optional] If provided, a pointer to the CHAR16 > array for the target variable name. > + Otherwise, will create a policy that targets an entire > namespace. > + @param[in] MinSize MinSize for the VariablePolicy. > + @param[in] MaxSize MaxSize for the VariablePolicy. > + @param[in] AttributesMustHave AttributesMustHave for the > VariablePolicy. > + @param[in] AttributesCantHave AttributesCantHave for the > VariablePolicy. > + @param[in] LockPolicyType LockPolicyType for the VariablePolicy. > + > + @retval EFI_INVALID_PARAMETER VariablePolicy pointer is NULL. > + @retval EFI_STATUS Status returned by CreateBasicVariablePolicy() > or RegisterVariablePolicy(). > + > +**/ > +EFI_STATUS > +EFIAPI > +RegisterBasicVariablePolicy ( > + IN VARIABLE_POLICY_PROTOCOL *VariablePolicy, > + IN CONST EFI_GUID *Namespace, > + IN CONST CHAR16 *Name OPTIONAL, > + IN UINT32 MinSize, > + IN UINT32 MaxSize, > + IN UINT32 AttributesMustHave, > + IN UINT32 AttributesCantHave, > + IN UINT8 LockPolicyType > + ) > +{ > + VARIABLE_POLICY_ENTRY *NewEntry; > + EFI_STATUS Status; > + > + // Check the simple things. > + if (VariablePolicy == NULL) { > + return EFI_INVALID_PARAMETER; > + } > + > + // Create the new entry and make sure that everything worked. > + NewEntry = NULL; > + Status = CreateBasicVariablePolicy( Namespace, > + Name, > + MinSize, > + MaxSize, > + AttributesMustHave, > + AttributesCantHave, > + LockPolicyType, > + &NewEntry ); > + > + // If that was successful, attempt to register the new policy. > + if (!EFI_ERROR( Status )) { > + Status = VariablePolicy->RegisterVariablePolicy( NewEntry ); } > + > + // If we allocated the buffer, free the buffer. > + if (NewEntry != NULL) { > + FreePool( NewEntry ); > + } > + > + return Status; > +} > + > + > +/** > + This helper function does everything that CreateBasicVariablePolicy() > +does, but also According to the description, it should do the CreateVarStateVariablePolicy(). This two register routine is same, can we merge it into one routine? Use RegisterVariablePolicy() to present it? > + uses the passed in protocol to register the policy with the infrastructure. > + Does not return a buffer, does not require the caller to free anything. > + > + @param[in] VariablePolicy Pointer to a valid instance of the VariablePolicy > protocol. > + @param[in] Namespace Pointer to an EFI_GUID for the target variable > namespace that this policy will protect. > + @param[in] Name [Optional] If provided, a pointer to the CHAR16 > array for the target variable name. > + Otherwise, will create a policy that targets an entire > namespace. > + @param[in] MinSize MinSize for the VariablePolicy. > + @param[in] MaxSize MaxSize for the VariablePolicy. > + @param[in] AttributesMustHave AttributesMustHave for the > VariablePolicy. > + @param[in] AttributesCantHave AttributesCantHave for the > VariablePolicy. > + @param[in] VarStateNamespace Pointer to the EFI_GUID for the > VARIABLE_LOCK_ON_VAR_STATE_POLICY.Namespace. > + @param[in] VarStateName Pointer to the CHAR16 array for the > VARIABLE_LOCK_ON_VAR_STATE_POLICY.Name. > + @param[in] VarStateValue Value for the > VARIABLE_LOCK_ON_VAR_STATE_POLICY.Value. > + > + @retval EFI_INVALID_PARAMETER VariablePolicy pointer is NULL. > + @retval EFI_STATUS Status returned by CreateBasicVariablePolicy() or > RegisterVariablePolicy(). > + > +**/ > +EFI_STATUS > +EFIAPI > +RegisterVarStateVariablePolicy ( > + IN VARIABLE_POLICY_PROTOCOL *VariablePolicy, > + IN CONST EFI_GUID *Namespace, > + IN CONST CHAR16 *Name OPTIONAL, > + IN UINT32 MinSize, > + IN UINT32 MaxSize, > + IN UINT32 AttributesMustHave, > + IN UINT32 AttributesCantHave, > + IN CONST EFI_GUID *VarStateNamespace, > + IN CONST CHAR16 *VarStateName, > + IN UINT8 VarStateValue > + ) > +{ > + VARIABLE_POLICY_ENTRY *NewEntry; > + EFI_STATUS Status; > + > + // Check the simple things. > + if (VariablePolicy == NULL) { > + return EFI_INVALID_PARAMETER; > + } > + > + // Create the new entry and make sure that everything worked. > + NewEntry = NULL; > + Status = CreateVarStateVariablePolicy( Namespace, > + Name, > + MinSize, > + MaxSize, > + AttributesMustHave, > + AttributesCantHave, > + VarStateNamespace, > + VarStateValue, > + VarStateName, > + &NewEntry ); > + > + // If that was successful, attempt to register the new policy. > + if (!EFI_ERROR( Status )) { > + Status = VariablePolicy->RegisterVariablePolicy( NewEntry ); } > + > + // If we allocated the buffer, free the buffer. > + if (NewEntry != NULL) { > + FreePool( NewEntry ); > + } > + > + return Status; > +} > diff --git a/MdeModulePkg/Include/Library/VariablePolicyHelperLib.h > b/MdeModulePkg/Include/Library/VariablePolicyHelperLib.h > new file mode 100644 > index 000000000000..721a55931aab > --- /dev/null > +++ b/MdeModulePkg/Include/Library/VariablePolicyHelperLib.h > @@ -0,0 +1,164 @@ > +/** @file -- VariablePolicyHelperLib.h > +This library contains helper functions for marshalling and registering > +new policies with the VariablePolicy infrastructure. > + > +Copyright (c) Microsoft Corporation. > +SPDX-License-Identifier: BSD-2-Clause-Patent > + > +**/ > + > +#ifndef _VARIABLE_POLICY_HELPER_LIB_H_ > +#define _VARIABLE_POLICY_HELPER_LIB_H_ > + > +#include <Protocol/VariablePolicy.h> > + > +/** > + This helper function will allocate and populate a new VariablePolicy > + structure for a policy that does not contain any sub-structures (such > +as > + VARIABLE_LOCK_ON_VAR_STATE_POLICY). > + > + NOTE: Caller will need to free structure once finished. > + > + @param[in] Namespace Pointer to an EFI_GUID for the target variable > namespace that this policy will protect. > + @param[in] Name [Optional] If provided, a pointer to the CHAR16 > array for the target variable name. > + Otherwise, will create a policy that targets an entire > namespace. > + @param[in] MinSize MinSize for the VariablePolicy. > + @param[in] MaxSize MaxSize for the VariablePolicy. > + @param[in] AttributesMustHave AttributesMustHave for the > VariablePolicy. > + @param[in] AttributesCantHave AttributesCantHave for the > VariablePolicy. > + @param[in] LockPolicyType LockPolicyType for the VariablePolicy. > + @param[out] NewEntry If successful, will be set to a pointer to the > allocated buffer containing the > + new policy. > + > + @retval EFI_SUCCESS Operation completed successfully and > structure is populated. > + @retval EFI_INVALID_PARAMETER Namespace is NULL. > + @retval EFI_INVALID_PARAMETER LockPolicyType is invalid for a basic > structure. > + @retval EFI_BUFFER_TOO_SMALL Finished structure would not fit in > UINT16 size. > + @retval EFI_OUT_OF_RESOURCES Could not allocate sufficient space > for structure. > + > +**/ > +EFI_STATUS > +EFIAPI > +CreateBasicVariablePolicy ( > + IN CONST EFI_GUID *Namespace, > + IN CONST CHAR16 *Name OPTIONAL, > + IN UINT32 MinSize, > + IN UINT32 MaxSize, > + IN UINT32 AttributesMustHave, > + IN UINT32 AttributesCantHave, > + IN UINT8 LockPolicyType, > + OUT VARIABLE_POLICY_ENTRY **NewEntry > + ); > + > + > +/** > + This helper function will allocate and populate a new VariablePolicy > + structure for a policy with a lock type of > VARIABLE_POLICY_TYPE_LOCK_ON_VAR_STATE. > + > + NOTE: Caller will need to free structure once finished. > + > + @param[in] Namespace Pointer to an EFI_GUID for the target variable > namespace that this policy will protect. > + @param[in] Name [Optional] If provided, a pointer to the CHAR16 > array for the target variable name. > + Otherwise, will create a policy that targets an entire > namespace. > + @param[in] MinSize MinSize for the VariablePolicy. > + @param[in] MaxSize MaxSize for the VariablePolicy. > + @param[in] AttributesMustHave AttributesMustHave for the > VariablePolicy. > + @param[in] AttributesCantHave AttributesCantHave for the > VariablePolicy. > + @param[in] VarStateNamespace Pointer to the EFI_GUID for the > VARIABLE_LOCK_ON_VAR_STATE_POLICY.Namespace. > + @param[in] VarStateValue Value for the > VARIABLE_LOCK_ON_VAR_STATE_POLICY.Value. > + @param[in] VarStateName Pointer to the CHAR16 array for the > VARIABLE_LOCK_ON_VAR_STATE_POLICY.Name. > + @param[out] NewEntry If successful, will be set to a pointer to the > allocated buffer containing the > + new policy. > + > + @retval EFI_SUCCESS Operation completed successfully and > structure is populated. > + @retval EFI_INVALID_PARAMETER Namespace, VarStateNamespace, > VarStateName is NULL. > + @retval EFI_BUFFER_TOO_SMALL Finished structure would not fit in > UINT16 size. > + @retval EFI_OUT_OF_RESOURCES Could not allocate sufficient space > for structure. > + > +**/ > +EFI_STATUS > +EFIAPI > +CreateVarStateVariablePolicy ( > + IN CONST EFI_GUID *Namespace, > + IN CONST CHAR16 *Name OPTIONAL, > + IN UINT32 MinSize, > + IN UINT32 MaxSize, > + IN UINT32 AttributesMustHave, > + IN UINT32 AttributesCantHave, > + IN CONST EFI_GUID *VarStateNamespace, > + IN UINT8 VarStateValue, > + IN CONST CHAR16 *VarStateName, > + OUT VARIABLE_POLICY_ENTRY **NewEntry > + ); > + > + > +/** > + This helper function does everything that CreateBasicVariablePolicy() > +does, but also > + uses the passed in protocol to register the policy with the infrastructure. > + Does not return a buffer, does not require the caller to free anything. > + > + @param[in] VariablePolicy Pointer to a valid instance of the VariablePolicy > protocol. > + @param[in] Namespace Pointer to an EFI_GUID for the target variable > namespace that this policy will protect. > + @param[in] Name [Optional] If provided, a pointer to the CHAR16 > array for the target variable name. > + Otherwise, will create a policy that targets an entire > namespace. > + @param[in] MinSize MinSize for the VariablePolicy. > + @param[in] MaxSize MaxSize for the VariablePolicy. > + @param[in] AttributesMustHave AttributesMustHave for the > VariablePolicy. > + @param[in] AttributesCantHave AttributesCantHave for the > VariablePolicy. > + @param[in] LockPolicyType LockPolicyType for the VariablePolicy. > + > + @retval EFI_INVALID_PARAMETER VariablePolicy pointer is NULL. > + @retval EFI_STATUS Status returned by CreateBasicVariablePolicy() > or RegisterVariablePolicy(). > + > +**/ > +EFI_STATUS > +EFIAPI > +RegisterBasicVariablePolicy ( > + IN VARIABLE_POLICY_PROTOCOL *VariablePolicy, > + IN CONST EFI_GUID *Namespace, > + IN CONST CHAR16 *Name OPTIONAL, > + IN UINT32 MinSize, > + IN UINT32 MaxSize, > + IN UINT32 AttributesMustHave, > + IN UINT32 AttributesCantHave, > + IN UINT8 LockPolicyType > + ); > + > + > +/** > + This helper function does everything that CreateBasicVariablePolicy() > +does, but also > + uses the passed in protocol to register the policy with the infrastructure. > + Does not return a buffer, does not require the caller to free anything. > + > + @param[in] VariablePolicy Pointer to a valid instance of the VariablePolicy > protocol. > + @param[in] Namespace Pointer to an EFI_GUID for the target variable > namespace that this policy will protect. > + @param[in] Name [Optional] If provided, a pointer to the CHAR16 > array for the target variable name. > + Otherwise, will create a policy that targets an entire > namespace. > + @param[in] MinSize MinSize for the VariablePolicy. > + @param[in] MaxSize MaxSize for the VariablePolicy. > + @param[in] AttributesMustHave AttributesMustHave for the > VariablePolicy. > + @param[in] AttributesCantHave AttributesCantHave for the > VariablePolicy. > + @param[in] VarStateNamespace Pointer to the EFI_GUID for the > VARIABLE_LOCK_ON_VAR_STATE_POLICY.Namespace. > + @param[in] VarStateName Pointer to the CHAR16 array for the > VARIABLE_LOCK_ON_VAR_STATE_POLICY.Name. > + @param[in] VarStateValue Value for the > VARIABLE_LOCK_ON_VAR_STATE_POLICY.Value. > + > + @retval EFI_INVALID_PARAMETER VariablePolicy pointer is NULL. > + @retval EFI_STATUS Status returned by CreateBasicVariablePolicy() or > RegisterVariablePolicy(). > + > +**/ > +EFI_STATUS > +EFIAPI > +RegisterVarStateVariablePolicy ( > + IN VARIABLE_POLICY_PROTOCOL *VariablePolicy, > + IN CONST EFI_GUID *Namespace, > + IN CONST CHAR16 *Name OPTIONAL, > + IN UINT32 MinSize, > + IN UINT32 MaxSize, > + IN UINT32 AttributesMustHave, > + IN UINT32 AttributesCantHave, > + IN CONST EFI_GUID *VarStateNamespace, > + IN CONST CHAR16 *VarStateName, > + IN UINT8 VarStateValue > + ); > + > +#endif // _VARIABLE_POLICY_HELPER_LIB_H_ > diff --git > a/MdeModulePkg/Library/VariablePolicyHelperLib/VariablePolicyHelperLib.i > nf > b/MdeModulePkg/Library/VariablePolicyHelperLib/VariablePolicyHelperLib.i > nf > new file mode 100644 > index 000000000000..551435dce8d3 > --- /dev/null > +++ > b/MdeModulePkg/Library/VariablePolicyHelperLib/VariablePolicyHelperL > +++ ib.inf > @@ -0,0 +1,36 @@ > +## @file VariablePolicyHelperLib.inf > +# This library contains helper functions for marshalling and > +registering # new policies with the VariablePolicy infrastructure. > +# > +# This library is currently written against VariablePolicy revision 0x00010000. > +# > +## > +# Copyright (c) Microsoft Corporation. > +# SPDX-License-Identifier: BSD-2-Clause-Patent ## > + > + > +[Defines] > + INF_VERSION = 0x00010017 > + BASE_NAME = VariablePolicyHelperLib > + # MODULE_UNI_FILE = VariablePolicyHelperLib.uni > + FILE_GUID = B3C2206B-FDD1-4AED-8352-FC5EC34C5630 > + VERSION_STRING = 1.0 > + MODULE_TYPE = BASE > + LIBRARY_CLASS = VariablePolicyHelperLib > + > + > +[Sources] > + VariablePolicyHelperLib.c > + > + > +[Packages] > + MdePkg/MdePkg.dec > + MdeModulePkg/MdeModulePkg.dec > + > + > +[LibraryClasses] > + BaseLib > + DebugLib > + MemoryAllocationLib > + BaseMemoryLib > diff --git > a/MdeModulePkg/Library/VariablePolicyHelperLib/VariablePolicyHelperLib.u > ni > b/MdeModulePkg/Library/VariablePolicyHelperLib/VariablePolicyHelperLib.u > ni > new file mode 100644 > index 000000000000..39cbf11a4ce9 > --- /dev/null > +++ > b/MdeModulePkg/Library/VariablePolicyHelperLib/VariablePolicyHelperL > +++ ib.uni > @@ -0,0 +1,12 @@ > +// /** @file > +// VariablePolicyHelperLib.uni > +// > +// Copyright (c) Microsoft Corporation. > +// SPDX-License-Identifier: BSD-2-Clause-Patent // // **/ > + > + > +#string STR_MODULE_ABSTRACT #language en-US "Library containing > helper functions for marshalling and registering new policies with the > VariablePolicy infrastructure" > + > +#string STR_MODULE_DESCRIPTION #language en-US "Library > containing helper functions for marshalling and registering new policies with > the VariablePolicy infrastructure" > diff --git a/MdeModulePkg/MdeModulePkg.dec > b/MdeModulePkg/MdeModulePkg.dec > index 990e23b07a08..8fd0a6a4fdfb 100644 > --- a/MdeModulePkg/MdeModulePkg.dec > +++ b/MdeModulePkg/MdeModulePkg.dec > @@ -147,6 +147,11 @@ > # > DisplayUpdateProgressLib|Include/Library/DisplayUpdateProgressLib.h > > + ## @libraryclass This library contains helper functions for marshalling and > + # registering new policies with the VariablePolicy infrastructure. > + # > + VariablePolicyHelperLib|Include/Library/VariablePolicyHelperLib.h > + > [Guids] > ## MdeModule package token space guid > # Include/Guid/MdeModulePkgTokenSpace.h > diff --git a/MdeModulePkg/MdeModulePkg.dsc > b/MdeModulePkg/MdeModulePkg.dsc > index 8501dae88eb1..c27a3b488a32 100644 > --- a/MdeModulePkg/MdeModulePkg.dsc > +++ b/MdeModulePkg/MdeModulePkg.dsc > @@ -99,6 +99,7 @@ > > BmpSupportLib|MdeModulePkg/Library/BaseBmpSupportLib/BaseBmpSupp > ortLib.inf > SafeIntLib|MdePkg/Library/BaseSafeIntLib/BaseSafeIntLib.inf > > DisplayUpdateProgressLib|MdeModulePkg/Library/DisplayUpdateProgressLi > bGraphics/DisplayUpdateProgressLibGraphics.inf > + > VariablePolicyHelperLib|MdeModulePkg/Library/VariablePolicyHelperLib/Va > riablePolicyHelperLib.inf > > [LibraryClasses.EBC.PEIM] > IoLib|MdePkg/Library/PeiIoLibCpuIo/PeiIoLibCpuIo.inf > @@ -224,6 +225,7 @@ > MdeModulePkg/Library/UefiHiiServicesLib/UefiHiiServicesLib.inf > MdeModulePkg/Library/BaseHobLibNull/BaseHobLibNull.inf > > MdeModulePkg/Library/BaseMemoryAllocationLibNull/BaseMemoryAllocati > onLibNull.inf > + > MdeModulePkg/Library/VariablePolicyHelperLib/VariablePolicyHelperLib.inf > > MdeModulePkg/Bus/Pci/PciHostBridgeDxe/PciHostBridgeDxe.inf > MdeModulePkg/Bus/Pci/PciSioSerialDxe/PciSioSerialDxe.inf > -- > 2.16.3.windows.1 > > > ^ permalink raw reply [flat|nested] 10+ messages in thread
* [PATCH v1 4/9] MdeModulePkg: Define the VarCheckPolicyLib and SMM interface [not found] <20200410183802.21192-1-michael.kubacki@outlook.com> 2020-04-10 18:37 ` [PATCH v1 2/9] MdeModulePkg: Define the VariablePolicyLib Michael Kubacki 2020-04-10 18:37 ` [PATCH v1 3/9] MdeModulePkg: Define the VariablePolicyHelperLib Michael Kubacki @ 2020-04-10 18:37 ` Michael Kubacki 2020-04-10 18:37 ` [PATCH v1 5/9] MdeModulePkg: Connect VariablePolicy business logic to VariableServices Michael Kubacki ` (4 subsequent siblings) 7 siblings, 0 replies; 10+ messages in thread From: Michael Kubacki @ 2020-04-10 18:37 UTC (permalink / raw) To: devel; +Cc: Jian J Wang, Hao A Wu, Liming Gao From: Bret Barkelew <brbarkel@microsoft.com> https://bugzilla.tianocore.org/show_bug.cgi?id=2522 VariablePolicy is an updated interface to replace VarLock and VarCheckProtocol. This is an instance of a VarCheckLib that is backed by the VariablePolicyLib business logic. It also publishes the SMM calling interface for messages from the DXE protocol. Cc: Jian J Wang <jian.j.wang@intel.com> Cc: Hao A Wu <hao.a.wu@intel.com> Cc: Liming Gao <liming.gao@intel.com> Signed-off-by: Bret Barkelew <brbarkel@microsoft.com> Signed-off-by: Michael Kubacki <michael.kubacki@microsoft.com> --- MdeModulePkg/Library/VarCheckPolicyLib/VarCheckPolicyLib.c | 211 ++++++++++++++++++++ MdeModulePkg/Include/Guid/VarCheckPolicyMmi.h | 43 ++++ MdeModulePkg/Library/VarCheckPolicyLib/VarCheckPolicyLib.inf | 44 ++++ MdeModulePkg/Library/VarCheckPolicyLib/VarCheckPolicyLib.uni | 12 ++ MdeModulePkg/MdeModulePkg.dec | 4 + MdeModulePkg/MdeModulePkg.dsc | 2 + 6 files changed, 316 insertions(+) diff --git a/MdeModulePkg/Library/VarCheckPolicyLib/VarCheckPolicyLib.c b/MdeModulePkg/Library/VarCheckPolicyLib/VarCheckPolicyLib.c new file mode 100644 index 000000000000..3ad396412c49 --- /dev/null +++ b/MdeModulePkg/Library/VarCheckPolicyLib/VarCheckPolicyLib.c @@ -0,0 +1,211 @@ +/** @file -- VarCheckPolicyLib.c +This is an instance of a VarCheck lib that leverages the business logic behind +the VariablePolicy code to make its decisions. + +Copyright (c) Microsoft Corporation. +SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include <Library/VarCheckLib.h> +#include <Library/BaseLib.h> +#include <Library/DebugLib.h> +#include <Library/SafeIntLib.h> +#include <Library/MmServicesTableLib.h> +#include <Library/BaseMemoryLib.h> +#include <Library/MemoryAllocationLib.h> + +#include <Protocol/VariablePolicy.h> +#include <Library/VariablePolicyLib.h> + +#include <Guid/VarCheckPolicyMmi.h> + +//================================================ +// As a VarCheck library, we're linked into the VariableServices +// and may not be able to call them indirectly. To get around this, +// use the internal GetVariable function to query the variable store. +//================================================ +EFI_STATUS +EFIAPI +VariableServiceGetVariable ( + IN CHAR16 *VariableName, + IN EFI_GUID *VendorGuid, + OUT UINT32 *Attributes OPTIONAL, + IN OUT UINTN *DataSize, + OUT VOID *Data + ); + + +/** + MM Communication Handler to recieve commands from the DXE protocol for + Variable Policies. This communication channel is used to register new policies + and poll and toggle the enforcement of variable policies. + + @param[in] DispatchHandle All parameters standard to MM communications convention. + @param[in] RegisterContex All parameters standard to MM communications convention. + @param[in,out] CommBuffer All parameters standard to MM communications convention. + @param[in,out] CommBufferSize All parameters standard to MM communications convention. + + @retval EFI_SUCCESS + @retval EFI_INVALID_PARAMETER CommBuffer or CommBufferSize is null pointer. + @retval EFI_INVALID_PARAMETER CommBuffer size is wrong. + @retval EFI_INVALID_PARAMETER Revision or signature don't match. + +**/ +STATIC +EFI_STATUS +EFIAPI +VarCheckPolicyLibMmiHandler ( + IN EFI_HANDLE DispatchHandle, + IN CONST VOID *RegisterContext, + IN OUT VOID *CommBuffer, + IN OUT UINTN *CommBufferSize + ) +{ + EFI_STATUS Status = EFI_SUCCESS; + VAR_CHECK_POLICY_COMM_HEADER *PolicyCommmHeader; + VAR_CHECK_POLICY_COMM_IS_ENABLED_PARAMS *IsEnabledParams; + // VAR_CHECK_POLICY_COMM_DUMP_PARAMS *DumpParams; // Not yet implemented. + VARIABLE_POLICY_ENTRY *PolicyEntry; + UINTN ExpectedSize; + + // + // Validate some input parameters. + // + // If either of the pointers are NULL, we can't proceed. + if (CommBuffer == NULL || CommBufferSize == NULL) { + DEBUG(( DEBUG_INFO, "%a - Invalid comm buffer pointers!\n", __FUNCTION__ )); + return EFI_INVALID_PARAMETER; + } + // If the size does not meet a minimum threshold, we cannot proceed. + ExpectedSize = sizeof(VAR_CHECK_POLICY_COMM_HEADER); + if (*CommBufferSize < ExpectedSize) { + DEBUG(( DEBUG_INFO, "%a - Bad comm buffer size! %d < %d\n", __FUNCTION__, *CommBufferSize, ExpectedSize )); + return EFI_INVALID_PARAMETER; + } + // Check the revision and the signature of the comm header. + PolicyCommmHeader = CommBuffer; + if (PolicyCommmHeader->Signature != VAR_CHECK_POLICY_COMM_SIG || + PolicyCommmHeader->Revision != VAR_CHECK_POLICY_COMM_REVISION) { + DEBUG(( DEBUG_INFO, "%a - Signature or revision are incorrect!\n", __FUNCTION__ )); + // We have verified the buffer is not null and have enough size to hold Result field. + PolicyCommmHeader->Result = EFI_INVALID_PARAMETER; + return EFI_SUCCESS; + } + + // + // Now we can process the command as it was sent. + // + PolicyCommmHeader->Result = EFI_ABORTED; // Set a default return for incomplete commands. + switch(PolicyCommmHeader->Command) { + case VAR_CHECK_POLICY_COMMAND_DISABLE: + PolicyCommmHeader->Result = DisableVariablePolicy(); + break; + + case VAR_CHECK_POLICY_COMMAND_IS_ENABLED: + // Make sure that we're dealing with a reasonable size. + // This add should be safe because these are fixed sizes so far. + ExpectedSize += sizeof(VAR_CHECK_POLICY_COMM_IS_ENABLED_PARAMS); + if (*CommBufferSize < ExpectedSize) { + DEBUG(( DEBUG_INFO, "%a - Bad comm buffer size! %d < %d\n", __FUNCTION__, *CommBufferSize, ExpectedSize )); + PolicyCommmHeader->Result = EFI_INVALID_PARAMETER; + break; + } + + // Now that we know we've got a valid size, we can fill in the rest of the data. + IsEnabledParams = (VAR_CHECK_POLICY_COMM_IS_ENABLED_PARAMS*)((UINT8*)CommBuffer + sizeof(VAR_CHECK_POLICY_COMM_HEADER)); + IsEnabledParams->State = IsVariablePolicyEnabled(); + PolicyCommmHeader->Result = EFI_SUCCESS; + break; + + case VAR_CHECK_POLICY_COMMAND_REGISTER: + // Make sure that we're dealing with a reasonable size. + // This add should be safe because these are fixed sizes so far. + ExpectedSize += sizeof(VARIABLE_POLICY_ENTRY); + if (*CommBufferSize < ExpectedSize) { + DEBUG(( DEBUG_INFO, "%a - Bad comm buffer size! %d < %d\n", __FUNCTION__, *CommBufferSize, ExpectedSize )); + PolicyCommmHeader->Result = EFI_INVALID_PARAMETER; + break; + } + + // At the very least, we can assume that we're working with a valid policy entry. + // Time to compare its internal size. + PolicyEntry = (VARIABLE_POLICY_ENTRY*)((UINT8*)CommBuffer + sizeof(VAR_CHECK_POLICY_COMM_HEADER)); + if (PolicyEntry->Version != VARIABLE_POLICY_ENTRY_REVISION || + PolicyEntry->Size < sizeof(VARIABLE_POLICY_ENTRY) || + EFI_ERROR(SafeUintnAdd(sizeof(VAR_CHECK_POLICY_COMM_HEADER), PolicyEntry->Size, &ExpectedSize)) || + *CommBufferSize < ExpectedSize) { + DEBUG(( DEBUG_INFO, "%a - Bad policy entry contents!\n", __FUNCTION__ )); + PolicyCommmHeader->Result = EFI_INVALID_PARAMETER; + break; + } + + PolicyCommmHeader->Result = RegisterVariablePolicy( PolicyEntry ); + break; + + case VAR_CHECK_POLICY_COMMAND_DUMP: + // There's currently no use for this, but it shouldn't be hard to implement. + PolicyCommmHeader->Result = EFI_UNSUPPORTED; + break; + + case VAR_CHECK_POLICY_COMMAND_LOCK: + PolicyCommmHeader->Result = LockVariablePolicy(); + break; + + default: + // Mark unknown requested command as EFI_UNSUPPORTED. + DEBUG(( DEBUG_INFO, "%a - Invalid command requested! %d\n", __FUNCTION__, PolicyCommmHeader->Command )); + PolicyCommmHeader->Result = EFI_UNSUPPORTED; + break; + } + + DEBUG(( DEBUG_VERBOSE, "%a - Command %d returning %r.\n", __FUNCTION__, + PolicyCommmHeader->Command, PolicyCommmHeader->Result )); + + return Status; +} + + +/** + Constructor function of VarCheckPolicyLib to register VarCheck handler and + SW MMI handlers. + + @param[in] ImageHandle The firmware allocated handle for the EFI image. + @param[in] SystemTable A pointer to the EFI System Table. + + @retval EFI_SUCCESS The constructor executed correctly. + +**/ +EFI_STATUS +EFIAPI +VarCheckPolicyLibConstructor ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + EFI_STATUS Status; + EFI_HANDLE DiscardedHandle; + + // Initialize the business logic with the internal GetVariable handler. + Status = InitVariablePolicyLib( VariableServiceGetVariable ); + + // Only proceed with init if the business logic could be initialized. + if (!EFI_ERROR( Status )) { + // Register the VarCheck handler for SetVariable filtering. + // Forward the check to the business logic of the library. + VarCheckLibRegisterSetVariableCheckHandler( ValidateSetVariable ); + + // Register the MMI handlers for receiving policy commands. + DiscardedHandle = NULL; + Status = gMmst->MmiHandlerRegister( VarCheckPolicyLibMmiHandler, + &gVarCheckPolicyLibMmiHandlerGuid, + &DiscardedHandle ); + } + // Otherwise, there's not much we can do. + else { + DEBUG(( DEBUG_ERROR, "%a - Cannot Initialize VariablePolicyLib! %r\n", __FUNCTION__, Status )); + ASSERT_EFI_ERROR( Status ); + } + + return Status; +} diff --git a/MdeModulePkg/Include/Guid/VarCheckPolicyMmi.h b/MdeModulePkg/Include/Guid/VarCheckPolicyMmi.h new file mode 100644 index 000000000000..ff64dcd9476b --- /dev/null +++ b/MdeModulePkg/Include/Guid/VarCheckPolicyMmi.h @@ -0,0 +1,43 @@ +/** @file -- VarCheckPolicyMmiCommon.h +This header contains communication definitions that are shared between DXE +and the MM component of VarCheckPolicy. + +Copyright (c) Microsoft Corporation. +SPDX-License-Identifier: BSD-2-Clause-Patent +**/ + +#ifndef _VAR_CHECK_POLICY_MMI_COMMON_H_ +#define _VAR_CHECK_POLICY_MMI_COMMON_H_ + +#define VAR_CHECK_POLICY_COMM_SIG SIGNATURE_32('V', 'C', 'P', 'C') +#define VAR_CHECK_POLICY_COMM_REVISION 1 + +#pragma pack(push, 1) + +typedef struct _VAR_CHECK_POLICY_COMM_HEADER { + UINT32 Signature; + UINT32 Revision; + UINT32 Command; + EFI_STATUS Result; +} VAR_CHECK_POLICY_COMM_HEADER; + +typedef struct _VAR_CHECK_POLICY_COMM_IS_ENABLED_PARAMS { + BOOLEAN State; +} VAR_CHECK_POLICY_COMM_IS_ENABLED_PARAMS; + +typedef struct _VAR_CHECK_POLICY_COMM_DUMP_PARAMS { + UINT32 Size; +} VAR_CHECK_POLICY_COMM_DUMP_PARAMS; + +#pragma pack(pop) + +// Make sure that we will hold at least the headers. +#define VAR_CHECK_POLICY_MIN_MM_BUFFER_SIZE MAX((OFFSET_OF(EFI_MM_COMMUNICATE_HEADER, Data) + sizeof(VAR_CHECK_POLICY_COMM_HEADER)), EFI_PAGES_TO_SIZE(4)) + +#define VAR_CHECK_POLICY_COMMAND_DISABLE 0x0001 +#define VAR_CHECK_POLICY_COMMAND_IS_ENABLED 0x0002 +#define VAR_CHECK_POLICY_COMMAND_REGISTER 0x0003 +#define VAR_CHECK_POLICY_COMMAND_DUMP 0x0004 +#define VAR_CHECK_POLICY_COMMAND_LOCK 0x0005 + +#endif // _VAR_CHECK_POLICY_MMI_COMMON_H_ diff --git a/MdeModulePkg/Library/VarCheckPolicyLib/VarCheckPolicyLib.inf b/MdeModulePkg/Library/VarCheckPolicyLib/VarCheckPolicyLib.inf new file mode 100644 index 000000000000..7c407e254330 --- /dev/null +++ b/MdeModulePkg/Library/VarCheckPolicyLib/VarCheckPolicyLib.inf @@ -0,0 +1,44 @@ +## @file VarCheckPolicyLib.inf +# This is an instance of a VarCheck lib that leverages the business logic behind +# the VariablePolicy code to make its decisions. +# +## +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = VarCheckPolicyLib + FILE_GUID = 9C28A48F-C884-4B1F-8B95-DEF125448023 + MODULE_TYPE = DXE_RUNTIME_DRIVER + VERSION_STRING = 1.0 + LIBRARY_CLASS = NULL|DXE_RUNTIME_DRIVER DXE_SMM_DRIVER + CONSTRUCTOR = VarCheckPolicyLibConstructor + + +[Sources] + VarCheckPolicyLib.c + + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + + +[LibraryClasses] + BaseLib + DebugLib + BaseMemoryLib + DxeServicesLib + MemoryAllocationLib + VarCheckLib + VariablePolicyLib + VariablePolicyHelperLib + SafeIntLib + MmServicesTableLib + + +[Guids] + gVarCheckPolicyLibMmiHandlerGuid ## CONSUME ## Used to register for MM Communication events. diff --git a/MdeModulePkg/Library/VarCheckPolicyLib/VarCheckPolicyLib.uni b/MdeModulePkg/Library/VarCheckPolicyLib/VarCheckPolicyLib.uni new file mode 100644 index 000000000000..eedeeed15d31 --- /dev/null +++ b/MdeModulePkg/Library/VarCheckPolicyLib/VarCheckPolicyLib.uni @@ -0,0 +1,12 @@ +// /** @file +// VarCheckPolicyLib.uni +// +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: BSD-2-Clause-Patent +// +// **/ + + +#string STR_MODULE_ABSTRACT #language en-US "NULL library implementation that conforms to the VarCheck interface to allow VariablePolicy engine to enforce policies" + +#string STR_MODULE_DESCRIPTION #language en-US "NULL library implementation that conforms to the VarCheck interface to allow VariablePolicy engine to enforce policies" diff --git a/MdeModulePkg/MdeModulePkg.dec b/MdeModulePkg/MdeModulePkg.dec index 8fd0a6a4fdfb..b6625b6c69ce 100644 --- a/MdeModulePkg/MdeModulePkg.dec +++ b/MdeModulePkg/MdeModulePkg.dec @@ -383,6 +383,10 @@ ## Include/Guid/EndofS3Resume.h gEdkiiEndOfS3ResumeGuid = { 0x96f5296d, 0x05f7, 0x4f3c, {0x84, 0x67, 0xe4, 0x56, 0x89, 0x0e, 0x0c, 0xb5 } } + ## Used (similar to Variable Services) to communicate policies to the enforcement engine. + # {DA1B0D11-D1A7-46C4-9DC9-F3714875C6EB} + gVarCheckPolicyLibMmiHandlerGuid = { 0xda1b0d11, 0xd1a7, 0x46c4, { 0x9d, 0xc9, 0xf3, 0x71, 0x48, 0x75, 0xc6, 0xeb }} + ## Include/Guid/S3SmmInitDone.h gEdkiiS3SmmInitDoneGuid = { 0x8f9d4825, 0x797d, 0x48fc, { 0x84, 0x71, 0x84, 0x50, 0x25, 0x79, 0x2e, 0xf6 } } diff --git a/MdeModulePkg/MdeModulePkg.dsc b/MdeModulePkg/MdeModulePkg.dsc index c27a3b488a32..7bdb1e0ee99c 100644 --- a/MdeModulePkg/MdeModulePkg.dsc +++ b/MdeModulePkg/MdeModulePkg.dsc @@ -311,6 +311,7 @@ MdeModulePkg/Library/TpmMeasurementLibNull/TpmMeasurementLibNull.inf MdeModulePkg/Library/AuthVariableLibNull/AuthVariableLibNull.inf MdeModulePkg/Library/VariablePolicyLib/VariablePolicyLib.inf + MdeModulePkg/Library/VarCheckPolicyLib/VarCheckPolicyLib.inf MdeModulePkg/Library/VarCheckLib/VarCheckLib.inf MdeModulePkg/Library/VarCheckHiiLib/VarCheckHiiLib.inf MdeModulePkg/Library/VarCheckPcdLib/VarCheckPcdLib.inf @@ -456,6 +457,7 @@ MdeModulePkg/Core/PiSmmCore/PiSmmCore.inf MdeModulePkg/Universal/Variable/RuntimeDxe/VariableSmm.inf { <LibraryClasses> + NULL|MdeModulePkg/Library/VarCheckPolicyLib/VarCheckPolicyLib.inf NULL|MdeModulePkg/Library/VarCheckUefiLib/VarCheckUefiLib.inf NULL|MdeModulePkg/Library/VarCheckHiiLib/VarCheckHiiLib.inf NULL|MdeModulePkg/Library/VarCheckPcdLib/VarCheckPcdLib.inf -- 2.16.3.windows.1 ^ permalink raw reply related [flat|nested] 10+ messages in thread
* [PATCH v1 5/9] MdeModulePkg: Connect VariablePolicy business logic to VariableServices [not found] <20200410183802.21192-1-michael.kubacki@outlook.com> ` (2 preceding siblings ...) 2020-04-10 18:37 ` [PATCH v1 4/9] MdeModulePkg: Define the VarCheckPolicyLib and SMM interface Michael Kubacki @ 2020-04-10 18:37 ` Michael Kubacki 2020-04-10 18:37 ` [PATCH v1 6/9] MdeModulePkg: Allow VariablePolicy state to delete protected variables Michael Kubacki ` (3 subsequent siblings) 7 siblings, 0 replies; 10+ messages in thread From: Michael Kubacki @ 2020-04-10 18:37 UTC (permalink / raw) To: devel; +Cc: Jian J Wang, Hao A Wu, Liming Gao From: Bret Barkelew <brbarkel@microsoft.com> https://bugzilla.tianocore.org/show_bug.cgi?id=2522 VariablePolicy is an updated interface to replace VarLock and VarCheckProtocol. Add connective code to publish the VariablePolicy protocol and wire it to either the SMM communication interface or directly into the VariablePolicyLib business logic. Cc: Jian J Wang <jian.j.wang@intel.com> Cc: Hao A Wu <hao.a.wu@intel.com> Cc: Liming Gao <liming.gao@intel.com> Signed-off-by: Bret Barkelew <brbarkel@microsoft.com> Signed-off-by: Michael Kubacki <michael.kubacki@microsoft.com> --- MdeModulePkg/Universal/Variable/RuntimeDxe/VariableDxe.c | 51 +++ MdeModulePkg/Universal/Variable/RuntimeDxe/VariablePolicySmmDxe.c | 445 ++++++++++++++++++++ MdeModulePkg/Universal/Variable/RuntimeDxe/VariableSmmRuntimeDxe.c | 15 + MdeModulePkg/Universal/Variable/RuntimeDxe/VariableSmm.inf | 3 + MdeModulePkg/Universal/Variable/RuntimeDxe/VariableSmmRuntimeDxe.inf | 8 + 5 files changed, 522 insertions(+) diff --git a/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableDxe.c b/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableDxe.c index 7d2b6c8e1fad..659382f9c88c 100644 --- a/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableDxe.c +++ b/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableDxe.c @@ -5,18 +5,34 @@ Copyright (C) 2013, Red Hat, Inc. Copyright (c) 2006 - 2019, Intel Corporation. All rights reserved.<BR> (C) Copyright 2015 Hewlett Packard Enterprise Development LP<BR> +Copyright (c) Microsoft Corporation. SPDX-License-Identifier: BSD-2-Clause-Patent **/ #include "Variable.h" +#include <Protocol/VariablePolicy.h> +#include <Library/VariablePolicyLib.h> + +EFI_STATUS +EFIAPI +ProtocolIsVariablePolicyEnabled ( + OUT BOOLEAN *State + ); + EFI_HANDLE mHandle = NULL; EFI_EVENT mVirtualAddressChangeEvent = NULL; VOID *mFtwRegistration = NULL; VOID ***mVarCheckAddressPointer = NULL; UINTN mVarCheckAddressPointerCount = 0; EDKII_VARIABLE_LOCK_PROTOCOL mVariableLock = { VariableLockRequestToLock }; +VARIABLE_POLICY_PROTOCOL mVariablePolicyProtocol = { VARIABLE_POLICY_PROTOCOL_REVISION, + DisableVariablePolicy, + ProtocolIsVariablePolicyEnabled, + RegisterVariablePolicy, + DumpVariablePolicy, + LockVariablePolicy }; EDKII_VAR_CHECK_PROTOCOL mVarCheck = { VarCheckRegisterSetVariableCheckHandler, VarCheckVariablePropertySet, VarCheckVariablePropertyGet }; @@ -303,6 +319,8 @@ OnReadyToBoot ( } } + ASSERT_EFI_ERROR (LockVariablePolicy ()); + gBS->CloseEvent (Event); } @@ -466,6 +484,28 @@ FtwNotificationEvent ( } +/** + This API function returns whether or not the policy engine is + currently being enforced. + + @param[out] State Pointer to a return value for whether the policy enforcement + is currently enabled. + + @retval EFI_SUCCESS + @retval Others An error has prevented this command from completing. + +**/ +EFI_STATUS +EFIAPI +ProtocolIsVariablePolicyEnabled ( + OUT BOOLEAN *State + ) +{ + *State = IsVariablePolicyEnabled (); + return EFI_SUCCESS; +} + + /** Variable Driver main entry point. The Variable driver places the 4 EFI runtime services in the EFI System Table and installs arch protocols @@ -576,6 +616,17 @@ VariableServiceInitialize ( ); ASSERT_EFI_ERROR (Status); + // Register and initialize the VariablePolicy engine. + Status = gBS->InstallMultipleProtocolInterfaces ( + &mHandle, + &gVariablePolicyProtocolGuid, + &mVariablePolicyProtocol, + NULL + ); + ASSERT_EFI_ERROR (Status); + Status = InitVariablePolicyLib (VariableServiceGetVariable); + ASSERT_EFI_ERROR (Status); + return EFI_SUCCESS; } diff --git a/MdeModulePkg/Universal/Variable/RuntimeDxe/VariablePolicySmmDxe.c b/MdeModulePkg/Universal/Variable/RuntimeDxe/VariablePolicySmmDxe.c new file mode 100644 index 000000000000..eecfc549c86f --- /dev/null +++ b/MdeModulePkg/Universal/Variable/RuntimeDxe/VariablePolicySmmDxe.c @@ -0,0 +1,445 @@ +/** @file -- VariablePolicySmmDxe.c +This protocol allows communication with Variable Policy Engine. + +Copyright (c) Microsoft Corporation. +SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include <Library/BaseLib.h> +#include <Library/UefiLib.h> +#include <Library/DebugLib.h> +#include <Library/SafeIntLib.h> +#include <Library/UefiBootServicesTableLib.h> +#include <Library/BaseMemoryLib.h> +#include <Library/MemoryAllocationLib.h> + +#include <Protocol/VariablePolicy.h> +#include <Protocol/MmCommunication.h> + +#include <Guid/PiSmmCommunicationRegionTable.h> +#include <Guid/VarCheckPolicyMmi.h> + +VARIABLE_POLICY_PROTOCOL mVariablePolicyProtocol; +EFI_MM_COMMUNICATION_PROTOCOL *mMmCommunication; + +VOID *mMmCommunicationBuffer; +UINTN mMmCommunicationBufferSize; + + +/** + This API function disables the variable policy enforcement. If it's + already been called once, will return EFI_ALREADY_STARTED. + + @retval EFI_SUCCESS + @retval EFI_ALREADY_STARTED Has already been called once this boot. + @retval EFI_WRITE_PROTECTED Interface has been locked until reboot. + +**/ +STATIC +EFI_STATUS +EFIAPI +ProtocolDisableVariablePolicy ( + VOID + ) +{ + EFI_STATUS Status; + EFI_MM_COMMUNICATE_HEADER *CommHeader; + VAR_CHECK_POLICY_COMM_HEADER *PolicyHeader; + UINTN BufferSize; + + // Set up the MM communication. + BufferSize = mMmCommunicationBufferSize; + CommHeader = mMmCommunicationBuffer; + PolicyHeader = (VAR_CHECK_POLICY_COMM_HEADER*)&CommHeader->Data; + CopyGuid( &CommHeader->HeaderGuid, &gVarCheckPolicyLibMmiHandlerGuid ); + CommHeader->MessageLength = BufferSize; + PolicyHeader->Signature = VAR_CHECK_POLICY_COMM_SIG; + PolicyHeader->Revision = VAR_CHECK_POLICY_COMM_REVISION; + PolicyHeader->Command = VAR_CHECK_POLICY_COMMAND_DISABLE; + + Status = mMmCommunication->Communicate( mMmCommunication, CommHeader, &BufferSize ); + DEBUG(( DEBUG_VERBOSE, "%a - MmCommunication returned %r.\n", __FUNCTION__, Status )); + + return (EFI_ERROR( Status )) ? Status : PolicyHeader->Result; +} + + +/** + This API function returns whether or not the policy engine is + currently being enforced. + + @param[out] State Pointer to a return value for whether the policy enforcement + is currently enabled. + + @retval EFI_SUCCESS + @retval Others An error has prevented this command from completing. + +**/ +STATIC +EFI_STATUS +EFIAPI +ProtocolIsVariablePolicyEnabled ( + OUT BOOLEAN *State + ) +{ + EFI_STATUS Status; + EFI_MM_COMMUNICATE_HEADER *CommHeader; + VAR_CHECK_POLICY_COMM_HEADER *PolicyHeader; + VAR_CHECK_POLICY_COMM_IS_ENABLED_PARAMS *CommandParams; + UINTN BufferSize; + + // Set up the MM communication. + BufferSize = mMmCommunicationBufferSize; + CommHeader = mMmCommunicationBuffer; + PolicyHeader = (VAR_CHECK_POLICY_COMM_HEADER*)&CommHeader->Data; + CommandParams = (VAR_CHECK_POLICY_COMM_IS_ENABLED_PARAMS*)(PolicyHeader + 1); + CopyGuid( &CommHeader->HeaderGuid, &gVarCheckPolicyLibMmiHandlerGuid ); + CommHeader->MessageLength = BufferSize; + PolicyHeader->Signature = VAR_CHECK_POLICY_COMM_SIG; + PolicyHeader->Revision = VAR_CHECK_POLICY_COMM_REVISION; + PolicyHeader->Command = VAR_CHECK_POLICY_COMMAND_IS_ENABLED; + + Status = mMmCommunication->Communicate( mMmCommunication, CommHeader, &BufferSize ); + DEBUG(( DEBUG_VERBOSE, "%a - MmCommunication returned %r.\n", __FUNCTION__, Status )); + + if (!EFI_ERROR( Status )) { + Status = PolicyHeader->Result; + *State = CommandParams->State; + } + + return Status; +} + + +/** + This API function validates and registers a new policy with + the policy enforcement engine. + + @param[in] NewPolicy Pointer to the incoming policy structure. + + @retval EFI_SUCCESS + @retval EFI_INVALID_PARAMETER NewPolicy is NULL or is internally inconsistent. + @retval EFI_ALREADY_STARTED An identical matching policy already exists. + @retval EFI_WRITE_PROTECTED The interface has been locked until the next reboot. + @retval EFI_UNSUPPORTED Policy enforcement has been disabled. No reason to add more policies. + @retval EFI_ABORTED A calculation error has prevented this function from completing. + @retval EFI_OUT_OF_RESOURCES Cannot grow the table to hold any more policies. + +**/ +STATIC +EFI_STATUS +EFIAPI +ProtocolRegisterVariablePolicy ( + IN VARIABLE_POLICY_ENTRY *NewPolicy + ) +{ + EFI_STATUS Status; + EFI_MM_COMMUNICATE_HEADER *CommHeader; + VAR_CHECK_POLICY_COMM_HEADER *PolicyHeader; + VOID *PolicyBuffer; + UINTN BufferSize; + UINTN RequiredSize; + + // First, make sure that the required size does not exceed the capabilities + // of the MmCommunication buffer. + RequiredSize = OFFSET_OF(EFI_MM_COMMUNICATE_HEADER, Data) + sizeof(VAR_CHECK_POLICY_COMM_HEADER); + Status = SafeUintnAdd( RequiredSize, NewPolicy->Size, &RequiredSize ); + if (EFI_ERROR( Status ) || RequiredSize > mMmCommunicationBufferSize) { + DEBUG(( DEBUG_ERROR, "%a - Policy too large for buffer! %r, %d > %d \n", __FUNCTION__, + Status, RequiredSize, mMmCommunicationBufferSize )); + return EFI_OUT_OF_RESOURCES; + } + + // Set up the MM communication. + BufferSize = mMmCommunicationBufferSize; + CommHeader = mMmCommunicationBuffer; + PolicyHeader = (VAR_CHECK_POLICY_COMM_HEADER*)&CommHeader->Data; + PolicyBuffer = (VOID*)(PolicyHeader + 1); + CopyGuid( &CommHeader->HeaderGuid, &gVarCheckPolicyLibMmiHandlerGuid ); + CommHeader->MessageLength = BufferSize; + PolicyHeader->Signature = VAR_CHECK_POLICY_COMM_SIG; + PolicyHeader->Revision = VAR_CHECK_POLICY_COMM_REVISION; + PolicyHeader->Command = VAR_CHECK_POLICY_COMMAND_REGISTER; + + // Copy the policy into place. This copy is safe because we've already tested above. + CopyMem( PolicyBuffer, NewPolicy, NewPolicy->Size ); + + Status = mMmCommunication->Communicate( mMmCommunication, CommHeader, &BufferSize ); + DEBUG(( DEBUG_VERBOSE, "%a - MmCommunication returned %r.\n", __FUNCTION__, Status )); + + return (EFI_ERROR( Status )) ? Status : PolicyHeader->Result; +} + + +/** + This API function will dump the entire contents of the variable policy table. + + Similar to GetVariable, the first call can be made with a 0 size and it will return + the size of the buffer required to hold the entire table. + + @param[out] Policy Pointer to the policy buffer. Can be NULL if Size is 0. + @param[in,out] Size On input, the size of the output buffer. On output, the size + of the data returned. + + @retval EFI_SUCCESS Policy data is in the output buffer and Size has been updated. + @retval EFI_INVALID_PARAMETER Size is NULL, or Size is non-zero and Policy is NULL. + @retval EFI_BUFFER_TOO_SMALL Size is insufficient to hold policy. Size updated with required size. + +**/ +STATIC +EFI_STATUS +EFIAPI +ProtocolDumpVariablePolicy ( + IN OUT UINT8 *Policy, + IN OUT UINT32 *Size + ) +{ + // There's currently no use for this, but it shouldn't be hard to implement. + return EFI_UNSUPPORTED; +} + + +/** + This API function locks the interface so that no more policy updates + can be performed or changes made to the enforcement until the next boot. + + @retval EFI_SUCCESS + @retval Others An error has prevented this command from completing. + +**/ +STATIC +EFI_STATUS +EFIAPI +ProtocolLockVariablePolicy ( + VOID + ) +{ + EFI_STATUS Status; + EFI_MM_COMMUNICATE_HEADER *CommHeader; + VAR_CHECK_POLICY_COMM_HEADER *PolicyHeader; + UINTN BufferSize; + + // Set up the MM communication. + BufferSize = mMmCommunicationBufferSize; + CommHeader = mMmCommunicationBuffer; + PolicyHeader = (VAR_CHECK_POLICY_COMM_HEADER*)&CommHeader->Data; + CopyGuid( &CommHeader->HeaderGuid, &gVarCheckPolicyLibMmiHandlerGuid ); + CommHeader->MessageLength = BufferSize; + PolicyHeader->Signature = VAR_CHECK_POLICY_COMM_SIG; + PolicyHeader->Revision = VAR_CHECK_POLICY_COMM_REVISION; + PolicyHeader->Command = VAR_CHECK_POLICY_COMMAND_LOCK; + + Status = mMmCommunication->Communicate( mMmCommunication, CommHeader, &BufferSize ); + DEBUG(( DEBUG_VERBOSE, "%a - MmCommunication returned %r.\n", __FUNCTION__, Status )); + + return (EFI_ERROR( Status )) ? Status : PolicyHeader->Result; +} + + +/** + This helper function locates the shared comm buffer and assigns it to input pointers. + + @param[in,out] BufferSize On input, the minimum buffer size required INCLUDING the MM communicate header. + On output, the size of the matching buffer found. + @param[out] LocatedBuffer A pointer to the matching buffer. + + @retval EFI_SUCCESS + @retval Others An error has prevented this command from completing. + +**/ +STATIC +EFI_STATUS +LocateMmCommonCommBuffer ( + IN OUT UINTN *BufferSize, + OUT VOID **LocatedBuffer + ) +{ + EFI_STATUS Status = EFI_ABORTED; + UINTN Index; + UINTN CurrentRegionSize; + EFI_MEMORY_DESCRIPTOR *SmmCommMemRegion; + EDKII_PI_SMM_COMMUNICATION_REGION_TABLE *PiSmmCommunicationRegionTable; + + // Make sure that we're working with good pointers. + if (BufferSize == NULL || LocatedBuffer == NULL) { + return EFI_INVALID_PARAMETER; + } + + Status = EfiGetSystemConfigurationTable( &gEdkiiPiSmmCommunicationRegionTableGuid, (VOID**)&PiSmmCommunicationRegionTable ); + if (EFI_ERROR( Status )) { + DEBUG((DEBUG_ERROR, "%a - Failed to get system configuration table! %r\n", __FUNCTION__, Status)); + return Status; + } + + // Walk through each of the entries trying to find one that will work for the target size. + Status = EFI_OUT_OF_RESOURCES; + CurrentRegionSize = 0; + SmmCommMemRegion = (EFI_MEMORY_DESCRIPTOR*)(PiSmmCommunicationRegionTable + 1); + for (Index = 0; Index < PiSmmCommunicationRegionTable->NumberOfEntries; Index++) { + if (SmmCommMemRegion->Type == EfiConventionalMemory) { + CurrentRegionSize = EFI_PAGES_TO_SIZE((UINTN)SmmCommMemRegion->NumberOfPages); + if (CurrentRegionSize >= *BufferSize) { + Status = EFI_SUCCESS; + break; + } + } + SmmCommMemRegion = (EFI_MEMORY_DESCRIPTOR*)((UINT8*)SmmCommMemRegion + PiSmmCommunicationRegionTable->DescriptorSize); + } + + if (!EFI_ERROR( Status )) { + *LocatedBuffer = (VOID*)(UINTN)SmmCommMemRegion->PhysicalStart; + *BufferSize = CurrentRegionSize; + } + else { + *LocatedBuffer = NULL; + *BufferSize = 0; + } + + return Status; +} + + +/** + This helper is responsible for telemetry and any other actions that + need to be taken if the VariablePolicy fails to lock. + + NOTE: It's possible that parts of this handling will need to become + part of a platform policy. + + @param[in] FailureStatus The failure that was reported by LockVariablePolicy + +**/ +STATIC +VOID +VariablePolicyHandleFailureToLock ( + IN EFI_STATUS FailureStatus + ) +{ + // For now, there's no agreed-upon policy for this. + return; +} + + +/** + ReadyToBoot Callback + Lock the VariablePolicy interface if it hasn't already been locked. + + @param[in] Event Event whose notification function is being invoked + @param[in] Context Pointer to the notification function's context + +**/ +STATIC +VOID +EFIAPI +LockPolicyInterfaceAtReadyToBoot ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + EFI_STATUS Status; + + Status = ProtocolLockVariablePolicy(); + + if (EFI_ERROR( Status )) { + VariablePolicyHandleFailureToLock( Status ); + } + else { + gBS->CloseEvent( Event ); + } + +} + + +/** + The driver's entry point. + + @param[in] ImageHandle The firmware allocated handle for the EFI image. + @param[in] SystemTable A pointer to the EFI System Table. + + @retval EFI_SUCCESS The entry point executed successfully. + @retval other Some error occured when executing this entry point. + +**/ +EFI_STATUS +EFIAPI +VariablePolicySmmDxeMain ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + EFI_STATUS Status = EFI_SUCCESS; + BOOLEAN ProtocolInstalled = FALSE; + BOOLEAN CallbackRegistered = FALSE; + EFI_EVENT ReadyToBootEvent; + + // Update the minimum buffer size. + mMmCommunicationBufferSize = VAR_CHECK_POLICY_MIN_MM_BUFFER_SIZE; + // Locate the shared comm buffer to use for sending MM commands. + Status = LocateMmCommonCommBuffer( &mMmCommunicationBufferSize, &mMmCommunicationBuffer ); + if (EFI_ERROR( Status )) { + DEBUG((DEBUG_ERROR, "%a - Failed to locate a viable MM comm buffer! %r\n", __FUNCTION__, Status)); + ASSERT_EFI_ERROR( Status ); + return Status; + } + + // Locate the MmCommunication protocol. + Status = gBS->LocateProtocol( &gEfiMmCommunicationProtocolGuid, NULL, (VOID**)&mMmCommunication ); + if (EFI_ERROR( Status )) { + DEBUG((DEBUG_ERROR, "%a - Failed to locate MmCommunication protocol! %r\n", __FUNCTION__, Status)); + ASSERT_EFI_ERROR( Status ); + return Status; + } + + // Configure the VariablePolicy protocol structure. + mVariablePolicyProtocol.Revision = VARIABLE_POLICY_PROTOCOL_REVISION; + mVariablePolicyProtocol.DisableVariablePolicy = ProtocolDisableVariablePolicy; + mVariablePolicyProtocol.IsVariablePolicyEnabled = ProtocolIsVariablePolicyEnabled; + mVariablePolicyProtocol.RegisterVariablePolicy = ProtocolRegisterVariablePolicy; + mVariablePolicyProtocol.DumpVariablePolicy = ProtocolDumpVariablePolicy; + mVariablePolicyProtocol.LockVariablePolicy = ProtocolLockVariablePolicy; + + // Register all the protocols and return the status. + Status = gBS->InstallMultipleProtocolInterfaces( &ImageHandle, + &gVariablePolicyProtocolGuid, &mVariablePolicyProtocol, + NULL ); + if (EFI_ERROR( Status )) { + DEBUG(( DEBUG_ERROR, "%a - Failed to install protocol! %r\n", __FUNCTION__, Status )); + goto Exit; + } + else { + ProtocolInstalled = TRUE; + } + + // + // Register a callback for ReadyToBoot so that the interface is at least locked before + // dispatching any bootloaders or UEFI apps. + Status = gBS->CreateEventEx( EVT_NOTIFY_SIGNAL, + TPL_CALLBACK, + LockPolicyInterfaceAtReadyToBoot, + NULL, + &gEfiEventReadyToBootGuid, + &ReadyToBootEvent ); + if (EFI_ERROR( Status )) { + DEBUG(( DEBUG_ERROR, "%a - Failed to create ReadyToBoot event! %r\n", __FUNCTION__, Status )); + goto Exit; + } + else { + CallbackRegistered = TRUE; + } + +Exit: + // + // If we're about to return a failed status (and unload this driver), we must first undo anything that + // has been successfully done. + if (EFI_ERROR( Status )) { + if (ProtocolInstalled) { + gBS->UninstallProtocolInterface( &ImageHandle, &gVariablePolicyProtocolGuid, &mVariablePolicyProtocol ); + } + if (CallbackRegistered) { + gBS->CloseEvent( ReadyToBootEvent ); + } + } + + return Status; +} diff --git a/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableSmmRuntimeDxe.c b/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableSmmRuntimeDxe.c index 2cf0ed32ae55..6bc576ecc47e 100644 --- a/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableSmmRuntimeDxe.c +++ b/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableSmmRuntimeDxe.c @@ -14,6 +14,7 @@ InitCommunicateBuffer() is really function to check the variable data size. Copyright (c) 2010 - 2019, Intel Corporation. All rights reserved.<BR> +Copyright (c) Microsoft Corporation. SPDX-License-Identifier: BSD-2-Clause-Patent **/ @@ -64,6 +65,17 @@ EFI_LOCK mVariableServicesLock; EDKII_VARIABLE_LOCK_PROTOCOL mVariableLock; EDKII_VAR_CHECK_PROTOCOL mVarCheck; +/** + The logic to initialize the VariablePolicy engine is in its own file. + +**/ +EFI_STATUS +EFIAPI +VariablePolicySmmDxeMain ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ); + /** Some Secure Boot Policy Variable may update following other variable changes(SecureBoot follows PK change, etc). Record their initial State when variable write service is ready. @@ -1791,6 +1803,9 @@ VariableSmmRuntimeInitialize ( &mVirtualAddressChangeEvent ); + // Initialize the VariablePolicy protocol and engine. + VariablePolicySmmDxeMain (ImageHandle, SystemTable); + return EFI_SUCCESS; } diff --git a/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableSmm.inf b/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableSmm.inf index bc3033588d40..bbc8d2080193 100644 --- a/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableSmm.inf +++ b/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableSmm.inf @@ -19,6 +19,7 @@ # the authentication service provided in this driver will be broken, and the behavior is undefined. # # Copyright (c) 2010 - 2019, Intel Corporation. All rights reserved.<BR> +# Copyright (c) Microsoft Corporation. # SPDX-License-Identifier: BSD-2-Clause-Patent # ## @@ -78,6 +79,8 @@ AuthVariableLib VarCheckLib UefiBootServicesTableLib + VariablePolicyLib + VariablePolicyHelperLib [Protocols] gEfiSmmFirmwareVolumeBlockProtocolGuid ## CONSUMES diff --git a/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableSmmRuntimeDxe.inf b/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableSmmRuntimeDxe.inf index 592862773390..5d7384ece5b2 100644 --- a/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableSmmRuntimeDxe.inf +++ b/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableSmmRuntimeDxe.inf @@ -14,6 +14,7 @@ # the authentication service provided in this driver will be broken, and the behavior is undefined. # # Copyright (c) 2010 - 2019, Intel Corporation. All rights reserved.<BR> +# Copyright (c) Microsoft Corporation.<BR> # SPDX-License-Identifier: BSD-2-Clause-Patent # ## @@ -42,6 +43,7 @@ VariableParsing.c VariableParsing.h Variable.h + VariablePolicySmmDxe.c [Packages] MdePkg/MdePkg.dec @@ -56,17 +58,20 @@ DxeServicesTableLib UefiDriverEntryPoint TpmMeasurementLib + SafeIntLib [Protocols] gEfiVariableWriteArchProtocolGuid ## PRODUCES gEfiVariableArchProtocolGuid ## PRODUCES gEfiSmmCommunicationProtocolGuid ## CONSUMES + gEfiMmCommunicationProtocolGuid ## CONSUMES ## CONSUMES ## NOTIFY ## UNDEFINED # Used to do smm communication gEfiSmmVariableProtocolGuid gEdkiiVariableLockProtocolGuid ## PRODUCES gEdkiiVarCheckProtocolGuid ## PRODUCES + gVariablePolicyProtocolGuid ## PRODUCES [FeaturePcd] gEfiMdeModulePkgTokenSpaceGuid.PcdEnableVariableRuntimeCache ## CONSUMES @@ -99,6 +104,9 @@ ## SOMETIMES_CONSUMES ## Variable:L"dbt" gEfiImageSecurityDatabaseGuid + gVarCheckPolicyLibMmiHandlerGuid + gEdkiiPiSmmCommunicationRegionTableGuid + [Depex] gEfiSmmCommunicationProtocolGuid -- 2.16.3.windows.1 ^ permalink raw reply related [flat|nested] 10+ messages in thread
* [PATCH v1 6/9] MdeModulePkg: Allow VariablePolicy state to delete protected variables [not found] <20200410183802.21192-1-michael.kubacki@outlook.com> ` (3 preceding siblings ...) 2020-04-10 18:37 ` [PATCH v1 5/9] MdeModulePkg: Connect VariablePolicy business logic to VariableServices Michael Kubacki @ 2020-04-10 18:37 ` Michael Kubacki 2020-04-10 18:38 ` [PATCH v1 7/9] SecurityPkg: Allow VariablePolicy state to delete authenticated variables Michael Kubacki ` (2 subsequent siblings) 7 siblings, 0 replies; 10+ messages in thread From: Michael Kubacki @ 2020-04-10 18:37 UTC (permalink / raw) To: devel; +Cc: Jian J Wang, Hao A Wu, Liming Gao From: Bret Barkelew <brbarkel@microsoft.com> https://bugzilla.tianocore.org/show_bug.cgi?id=2522 TcgMorLockSmm provides special protections for the TCG MOR variables. This will check IsVariablePolicyEnabled() before enforcing them to allow variable deletion when policy engine is disabled. Only allows deletion, not modification. Cc: Jian J Wang <jian.j.wang@intel.com> Cc: Hao A Wu <hao.a.wu@intel.com> Cc: Liming Gao <liming.gao@intel.com> Signed-off-by: Bret Barkelew <brbarkel@microsoft.com> Signed-off-by: Michael Kubacki <michael.kubacki@microsoft.com> --- MdeModulePkg/Universal/Variable/RuntimeDxe/TcgMorLockSmm.c | 10 ++++++++++ MdeModulePkg/Universal/Variable/RuntimeDxe/VariableStandaloneMm.inf | 2 ++ 2 files changed, 12 insertions(+) diff --git a/MdeModulePkg/Universal/Variable/RuntimeDxe/TcgMorLockSmm.c b/MdeModulePkg/Universal/Variable/RuntimeDxe/TcgMorLockSmm.c index 6d80eb64341a..085f82035f4b 100644 --- a/MdeModulePkg/Universal/Variable/RuntimeDxe/TcgMorLockSmm.c +++ b/MdeModulePkg/Universal/Variable/RuntimeDxe/TcgMorLockSmm.c @@ -5,6 +5,7 @@ This module adds Variable Hook and check MemoryOverwriteRequestControlLock. Copyright (c) 2016 - 2018, Intel Corporation. All rights reserved.<BR> +Copyright (c) Microsoft Corporation. SPDX-License-Identifier: BSD-2-Clause-Patent **/ @@ -17,6 +18,10 @@ SPDX-License-Identifier: BSD-2-Clause-Patent #include <Library/BaseMemoryLib.h> #include "Variable.h" +#include <Protocol/VariablePolicy.h> + +#include <Library/VariablePolicyLib.h> + typedef struct { CHAR16 *VariableName; EFI_GUID *VendorGuid; @@ -341,6 +346,11 @@ SetVariableCheckHandlerMor ( return EFI_SUCCESS; } + // Permit deletion when policy is disabled. + if (!IsVariablePolicyEnabled() && ((Attributes == 0) || (DataSize == 0))) { + return EFI_SUCCESS; + } + // // MorLock variable // diff --git a/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableStandaloneMm.inf b/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableStandaloneMm.inf index 6e17f6cdf588..d8f480be27cc 100644 --- a/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableStandaloneMm.inf +++ b/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableStandaloneMm.inf @@ -20,6 +20,7 @@ # # Copyright (c) 2010 - 2019, Intel Corporation. All rights reserved.<BR> # Copyright (c) 2018, Linaro, Ltd. All rights reserved.<BR> +# Copyright (c) Microsoft Corporation. # SPDX-License-Identifier: BSD-2-Clause-Patent # ## @@ -74,6 +75,7 @@ StandaloneMmDriverEntryPoint SynchronizationLib VarCheckLib + VariablePolicyLib [Protocols] gEfiSmmFirmwareVolumeBlockProtocolGuid ## CONSUMES -- 2.16.3.windows.1 ^ permalink raw reply related [flat|nested] 10+ messages in thread
* [PATCH v1 7/9] SecurityPkg: Allow VariablePolicy state to delete authenticated variables [not found] <20200410183802.21192-1-michael.kubacki@outlook.com> ` (4 preceding siblings ...) 2020-04-10 18:37 ` [PATCH v1 6/9] MdeModulePkg: Allow VariablePolicy state to delete protected variables Michael Kubacki @ 2020-04-10 18:38 ` Michael Kubacki 2020-04-10 18:38 ` [PATCH v1 8/9] MdeModulePkg: Change TCG MOR variables to use VariablePolicy Michael Kubacki 2020-04-10 18:38 ` [PATCH v1 9/9] MdeModulePkg: Drop VarLock from RuntimeDxe variable driver Michael Kubacki 7 siblings, 0 replies; 10+ messages in thread From: Michael Kubacki @ 2020-04-10 18:38 UTC (permalink / raw) To: devel; +Cc: Jiewen Yao, Jian J Wang, Chao Zhang From: Bret Barkelew <brbarkel@microsoft.com> https://bugzilla.tianocore.org/show_bug.cgi?id=2522 Causes AuthService to check IsVariablePolicyEnabled() before enforcing write protections to allow variable deletion when policy engine is disabled. Only allows deletion, not modification. Cc: Jiewen Yao <jiewen.yao@intel.com> Cc: Jian J Wang <jian.j.wang@intel.com> Cc: Chao Zhang <chao.b.zhang@intel.com> Signed-off-by: Bret Barkelew <brbarkel@microsoft.com> Signed-off-by: Michael Kubacki <michael.kubacki@microsoft.com> --- SecurityPkg/Library/AuthVariableLib/AuthService.c | 22 ++++++++++++++++---- SecurityPkg/Library/AuthVariableLib/AuthVariableLib.inf | 2 ++ 2 files changed, 20 insertions(+), 4 deletions(-) diff --git a/SecurityPkg/Library/AuthVariableLib/AuthService.c b/SecurityPkg/Library/AuthVariableLib/AuthService.c index 2f60331f2c04..aca9a5620c28 100644 --- a/SecurityPkg/Library/AuthVariableLib/AuthService.c +++ b/SecurityPkg/Library/AuthVariableLib/AuthService.c @@ -19,12 +19,16 @@ 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. // @@ -217,9 +221,12 @@ NeedPhysicallyPresent( IN EFI_GUID *VendorGuid ) { - if ((CompareGuid (VendorGuid, &gEfiSecureBootEnableDisableGuid) && (StrCmp (VariableName, EFI_SECURE_BOOT_ENABLE_NAME) == 0)) - || (CompareGuid (VendorGuid, &gEfiCustomModeEnableGuid) && (StrCmp (VariableName, EFI_CUSTOM_MODE_NAME) == 0))) { - return TRUE; + // 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; @@ -842,7 +849,8 @@ ProcessVariable ( &OrgVariableInfo ); - if ((!EFI_ERROR (Status)) && IsDeleteAuthVariable (OrgVariableInfo.Attributes, Data, DataSize, Attributes) && UserPhysicalPresent()) { + // 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. // @@ -1960,6 +1968,12 @@ VerifyTimeBasedPayload ( CopyMem (Buffer, PayloadPtr, PayloadSize); + // 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; + } + if (AuthVarType == AuthVarTypePk) { // // Verify that the signature has been made with the current Platform Key (no chaining for PK). diff --git a/SecurityPkg/Library/AuthVariableLib/AuthVariableLib.inf b/SecurityPkg/Library/AuthVariableLib/AuthVariableLib.inf index 8d4ce14df494..8eadeebcebd7 100644 --- a/SecurityPkg/Library/AuthVariableLib/AuthVariableLib.inf +++ b/SecurityPkg/Library/AuthVariableLib/AuthVariableLib.inf @@ -3,6 +3,7 @@ # # Copyright (c) 2015 - 2016, Intel Corporation. All rights reserved.<BR> # Copyright (c) 2018, ARM Limited. All rights reserved.<BR> +# Copyright (c) Microsoft Corporation. # # SPDX-License-Identifier: BSD-2-Clause-Patent # @@ -41,6 +42,7 @@ MemoryAllocationLib BaseCryptLib PlatformSecureLib + VariablePolicyLib [Guids] ## CONSUMES ## Variable:L"SetupMode" -- 2.16.3.windows.1 ^ permalink raw reply related [flat|nested] 10+ messages in thread
* [PATCH v1 8/9] MdeModulePkg: Change TCG MOR variables to use VariablePolicy [not found] <20200410183802.21192-1-michael.kubacki@outlook.com> ` (5 preceding siblings ...) 2020-04-10 18:38 ` [PATCH v1 7/9] SecurityPkg: Allow VariablePolicy state to delete authenticated variables Michael Kubacki @ 2020-04-10 18:38 ` Michael Kubacki 2020-04-10 18:38 ` [PATCH v1 9/9] MdeModulePkg: Drop VarLock from RuntimeDxe variable driver Michael Kubacki 7 siblings, 0 replies; 10+ messages in thread From: Michael Kubacki @ 2020-04-10 18:38 UTC (permalink / raw) To: devel; +Cc: Jian J Wang, Hao A Wu, Liming Gao From: Bret Barkelew <brbarkel@microsoft.com> https://bugzilla.tianocore.org/show_bug.cgi?id=2522 These were previously using VarLock, which is being deprecated. Cc: Jian J Wang <jian.j.wang@intel.com> Cc: Hao A Wu <hao.a.wu@intel.com> Cc: Liming Gao <liming.gao@intel.com> Signed-off-by: Bret Barkelew <brbarkel@microsoft.com> Signed-off-by: Michael Kubacki <michael.kubacki@microsoft.com> --- MdeModulePkg/Universal/Variable/RuntimeDxe/TcgMorLockDxe.c | 52 ++++++++++++++------ MdeModulePkg/Universal/Variable/RuntimeDxe/TcgMorLockSmm.c | 52 +++++++++++++++----- MdeModulePkg/Universal/Variable/RuntimeDxe/VariableRuntimeDxe.inf | 3 ++ MdeModulePkg/Universal/Variable/RuntimeDxe/VariableStandaloneMm.inf | 1 + 4 files changed, 83 insertions(+), 25 deletions(-) diff --git a/MdeModulePkg/Universal/Variable/RuntimeDxe/TcgMorLockDxe.c b/MdeModulePkg/Universal/Variable/RuntimeDxe/TcgMorLockDxe.c index e7accf4ed806..cac094532a91 100644 --- a/MdeModulePkg/Universal/Variable/RuntimeDxe/TcgMorLockDxe.c +++ b/MdeModulePkg/Universal/Variable/RuntimeDxe/TcgMorLockDxe.c @@ -5,6 +5,7 @@ MOR lock control unsupported. Copyright (c) 2016, Intel Corporation. All rights reserved.<BR> +Copyright (c) Microsoft Corporation. SPDX-License-Identifier: BSD-2-Clause-Patent **/ @@ -17,7 +18,8 @@ SPDX-License-Identifier: BSD-2-Clause-Patent #include <Library/BaseMemoryLib.h> #include "Variable.h" -extern EDKII_VARIABLE_LOCK_PROTOCOL mVariableLock; +#include <Protocol/VariablePolicy.h> +#include <Library/VariablePolicyHelperLib.h> /** This service is an MOR/MorLock checker handler for the SetVariable(). @@ -77,11 +79,6 @@ MorLockInit ( NULL // Data ); - // - // Need set this variable to be read-only to prevent other module set it. - // - VariableLockRequestToLock (&mVariableLock, MEMORY_OVERWRITE_REQUEST_CONTROL_LOCK_NAME, &gEfiMemoryOverwriteRequestControlLockGuid); - // // The MOR variable can effectively improve platform security only when the // MorLock variable protects the MOR variable. In turn MorLock cannot be made @@ -99,11 +96,6 @@ MorLockInit ( 0, // DataSize NULL // Data ); - VariableLockRequestToLock ( - &mVariableLock, - MEMORY_OVERWRITE_REQUEST_VARIABLE_NAME, - &gEfiMemoryOverwriteControlDataGuid - ); return EFI_SUCCESS; } @@ -118,7 +110,39 @@ MorLockInitAtEndOfDxe ( VOID ) { - // - // Do nothing. - // + EFI_STATUS Status; + VARIABLE_POLICY_PROTOCOL *VariablePolicy; + + // First, we obviously need to locate the VariablePolicy protocol. + Status = gBS->LocateProtocol( &gVariablePolicyProtocolGuid, NULL, (VOID**)&VariablePolicy ); + if (EFI_ERROR( Status )) { + DEBUG(( DEBUG_ERROR, "%a - Could not locate VariablePolicy protocol! %r\n", __FUNCTION__, Status )); + return; + } + + // If we're successful, go ahead and set the policies to protect the target variables. + Status = RegisterBasicVariablePolicy( VariablePolicy, + &gEfiMemoryOverwriteRequestControlLockGuid, + MEMORY_OVERWRITE_REQUEST_CONTROL_LOCK_NAME, + VARIABLE_POLICY_NO_MIN_SIZE, + VARIABLE_POLICY_NO_MAX_SIZE, + VARIABLE_POLICY_NO_MUST_ATTR, + VARIABLE_POLICY_NO_CANT_ATTR, + VARIABLE_POLICY_TYPE_LOCK_NOW ); + if (EFI_ERROR( Status )) { + DEBUG(( DEBUG_ERROR, "%a - Could not lock variable %s! %r\n", __FUNCTION__, MEMORY_OVERWRITE_REQUEST_CONTROL_LOCK_NAME, Status )); + } + Status = RegisterBasicVariablePolicy( VariablePolicy, + &gEfiMemoryOverwriteControlDataGuid, + MEMORY_OVERWRITE_REQUEST_VARIABLE_NAME, + VARIABLE_POLICY_NO_MIN_SIZE, + VARIABLE_POLICY_NO_MAX_SIZE, + VARIABLE_POLICY_NO_MUST_ATTR, + VARIABLE_POLICY_NO_CANT_ATTR, + VARIABLE_POLICY_TYPE_LOCK_NOW ); + if (EFI_ERROR( Status )) { + DEBUG(( DEBUG_ERROR, "%a - Could not lock variable %s! %r\n", __FUNCTION__, MEMORY_OVERWRITE_REQUEST_VARIABLE_NAME, Status )); + } + + return; } diff --git a/MdeModulePkg/Universal/Variable/RuntimeDxe/TcgMorLockSmm.c b/MdeModulePkg/Universal/Variable/RuntimeDxe/TcgMorLockSmm.c index 085f82035f4b..ee37942a6b0c 100644 --- a/MdeModulePkg/Universal/Variable/RuntimeDxe/TcgMorLockSmm.c +++ b/MdeModulePkg/Universal/Variable/RuntimeDxe/TcgMorLockSmm.c @@ -19,7 +19,7 @@ SPDX-License-Identifier: BSD-2-Clause-Patent #include "Variable.h" #include <Protocol/VariablePolicy.h> - +#include <Library/VariablePolicyHelperLib.h> #include <Library/VariablePolicyLib.h> typedef struct { @@ -422,6 +422,8 @@ MorLockInitAtEndOfDxe ( { UINTN MorSize; EFI_STATUS MorStatus; + EFI_STATUS Status; + VARIABLE_POLICY_ENTRY *NewPolicy; if (!mMorLockInitializationRequired) { // @@ -494,11 +496,25 @@ MorLockInitAtEndOfDxe ( // The MOR variable is absent; the platform firmware does not support it. // Lock the variable so that no other module may create it. // - VariableLockRequestToLock ( - NULL, // This - MEMORY_OVERWRITE_REQUEST_VARIABLE_NAME, - &gEfiMemoryOverwriteControlDataGuid - ); + NewPolicy = NULL; + Status = CreateBasicVariablePolicy( &gEfiMemoryOverwriteControlDataGuid, + MEMORY_OVERWRITE_REQUEST_VARIABLE_NAME, + VARIABLE_POLICY_NO_MIN_SIZE, + VARIABLE_POLICY_NO_MAX_SIZE, + VARIABLE_POLICY_NO_MUST_ATTR, + VARIABLE_POLICY_NO_CANT_ATTR, + VARIABLE_POLICY_TYPE_LOCK_NOW, + &NewPolicy ); + if (!EFI_ERROR( Status )) { + Status = RegisterVariablePolicy( NewPolicy ); + } + if (EFI_ERROR( Status )) { + DEBUG(( DEBUG_ERROR, "%a - Failed to lock variable %s! %r\n", __FUNCTION__, MEMORY_OVERWRITE_REQUEST_VARIABLE_NAME, Status )); + ASSERT_EFI_ERROR( Status ); + } + if (NewPolicy != NULL) { + FreePool( NewPolicy ); + } // // Delete the MOR Control Lock variable too (should it exists for some @@ -514,9 +530,23 @@ MorLockInitAtEndOfDxe ( ); mMorLockPassThru = FALSE; - VariableLockRequestToLock ( - NULL, // This - MEMORY_OVERWRITE_REQUEST_CONTROL_LOCK_NAME, - &gEfiMemoryOverwriteRequestControlLockGuid - ); + NewPolicy = NULL; + Status = CreateBasicVariablePolicy( &gEfiMemoryOverwriteRequestControlLockGuid, + MEMORY_OVERWRITE_REQUEST_CONTROL_LOCK_NAME, + VARIABLE_POLICY_NO_MIN_SIZE, + VARIABLE_POLICY_NO_MAX_SIZE, + VARIABLE_POLICY_NO_MUST_ATTR, + VARIABLE_POLICY_NO_CANT_ATTR, + VARIABLE_POLICY_TYPE_LOCK_NOW, + &NewPolicy ); + if (!EFI_ERROR( Status )) { + Status = RegisterVariablePolicy( NewPolicy ); + } + if (EFI_ERROR( Status )) { + DEBUG(( DEBUG_ERROR, "%a - Failed to lock variable %s! %r\n", __FUNCTION__, MEMORY_OVERWRITE_REQUEST_CONTROL_LOCK_NAME, Status )); + ASSERT_EFI_ERROR( Status ); + } + if (NewPolicy != NULL) { + FreePool( NewPolicy ); + } } diff --git a/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableRuntimeDxe.inf b/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableRuntimeDxe.inf index ceea5d1ff9ac..5fbec5cee8ab 100644 --- a/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableRuntimeDxe.inf +++ b/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableRuntimeDxe.inf @@ -10,6 +10,7 @@ # buffer overflow or integer overflow. # # Copyright (c) 2006 - 2019, Intel Corporation. All rights reserved.<BR> +# Copyright (c) Microsoft Corporation. # SPDX-License-Identifier: BSD-2-Clause-Patent # ## @@ -69,6 +70,7 @@ TpmMeasurementLib AuthVariableLib VarCheckLib + VariablePolicyHelperLib [Protocols] gEfiFirmwareVolumeBlockProtocolGuid ## CONSUMES @@ -78,6 +80,7 @@ gEfiVariableWriteArchProtocolGuid ## PRODUCES gEfiVariableArchProtocolGuid ## PRODUCES gEdkiiVariableLockProtocolGuid ## PRODUCES + gVariablePolicyProtocolGuid ## CONSUMES gEdkiiVarCheckProtocolGuid ## PRODUCES [Guids] diff --git a/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableStandaloneMm.inf b/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableStandaloneMm.inf index d8f480be27cc..62f2f9252f43 100644 --- a/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableStandaloneMm.inf +++ b/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableStandaloneMm.inf @@ -76,6 +76,7 @@ SynchronizationLib VarCheckLib VariablePolicyLib + VariablePolicyHelperLib [Protocols] gEfiSmmFirmwareVolumeBlockProtocolGuid ## CONSUMES -- 2.16.3.windows.1 ^ permalink raw reply related [flat|nested] 10+ messages in thread
* [PATCH v1 9/9] MdeModulePkg: Drop VarLock from RuntimeDxe variable driver [not found] <20200410183802.21192-1-michael.kubacki@outlook.com> ` (6 preceding siblings ...) 2020-04-10 18:38 ` [PATCH v1 8/9] MdeModulePkg: Change TCG MOR variables to use VariablePolicy Michael Kubacki @ 2020-04-10 18:38 ` Michael Kubacki 7 siblings, 0 replies; 10+ messages in thread From: Michael Kubacki @ 2020-04-10 18:38 UTC (permalink / raw) To: devel; +Cc: Jian J Wang, Hao A Wu, Liming Gao From: Bret Barkelew <brbarkel@microsoft.com> https://bugzilla.tianocore.org/show_bug.cgi?id=2522 Now that everything should be moved to VariablePolicy, drop support for the deprecated VarLock SMI interface and associated functions from variable RuntimeDxe. Cc: Jian J Wang <jian.j.wang@intel.com> Cc: Hao A Wu <hao.a.wu@intel.com> Cc: Liming Gao <liming.gao@intel.com> Signed-off-by: Bret Barkelew <brbarkel@microsoft.com> Signed-off-by: Michael Kubacki <michael.kubacki@microsoft.com> --- MdeModulePkg/Universal/Variable/RuntimeDxe/VarCheck.c | 49 +------------- MdeModulePkg/Universal/Variable/RuntimeDxe/VariableLockRequstToLock.c | 71 ++++++++++++++++++++ MdeModulePkg/Universal/Variable/RuntimeDxe/VariableRuntimeDxe.inf | 2 + MdeModulePkg/Universal/Variable/RuntimeDxe/VariableSmm.inf | 1 + MdeModulePkg/Universal/Variable/RuntimeDxe/VariableStandaloneMm.inf | 1 + 5 files changed, 76 insertions(+), 48 deletions(-) diff --git a/MdeModulePkg/Universal/Variable/RuntimeDxe/VarCheck.c b/MdeModulePkg/Universal/Variable/RuntimeDxe/VarCheck.c index f15219df5eb8..486d85b022e1 100644 --- a/MdeModulePkg/Universal/Variable/RuntimeDxe/VarCheck.c +++ b/MdeModulePkg/Universal/Variable/RuntimeDxe/VarCheck.c @@ -3,60 +3,13 @@ and variable lock protocol based on VarCheckLib. Copyright (c) 2015, Intel Corporation. All rights reserved.<BR> +Copyright (c) Microsoft Corporation. SPDX-License-Identifier: BSD-2-Clause-Patent **/ #include "Variable.h" -/** - Mark a variable that will become read-only after leaving the DXE phase of execution. - Write request coming from SMM environment through EFI_SMM_VARIABLE_PROTOCOL is allowed. - - @param[in] This The VARIABLE_LOCK_PROTOCOL instance. - @param[in] VariableName A pointer to the variable name that will be made read-only subsequently. - @param[in] VendorGuid A pointer to the vendor GUID that will be made read-only subsequently. - - @retval EFI_SUCCESS The variable specified by the VariableName and the VendorGuid was marked - as pending to be read-only. - @retval EFI_INVALID_PARAMETER VariableName or VendorGuid is NULL. - Or VariableName is an empty string. - @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 to hold the lock request. -**/ -EFI_STATUS -EFIAPI -VariableLockRequestToLock ( - IN CONST EDKII_VARIABLE_LOCK_PROTOCOL *This, - IN CHAR16 *VariableName, - IN EFI_GUID *VendorGuid - ) -{ - EFI_STATUS Status; - VAR_CHECK_VARIABLE_PROPERTY Property; - - AcquireLockOnlyAtBootTime (&mVariableModuleGlobal->VariableGlobal.VariableServicesLock); - - Status = VarCheckLibVariablePropertyGet (VariableName, VendorGuid, &Property); - if (!EFI_ERROR (Status)) { - Property.Property |= VAR_CHECK_VARIABLE_PROPERTY_READ_ONLY; - } else { - Property.Revision = VAR_CHECK_VARIABLE_PROPERTY_REVISION; - Property.Property = VAR_CHECK_VARIABLE_PROPERTY_READ_ONLY; - Property.Attributes = 0; - Property.MinSize = 1; - Property.MaxSize = MAX_UINTN; - } - Status = VarCheckLibVariablePropertySet (VariableName, VendorGuid, &Property); - - DEBUG ((EFI_D_INFO, "[Variable] Lock: %g:%s %r\n", VendorGuid, VariableName, Status)); - - ReleaseLockOnlyAtBootTime (&mVariableModuleGlobal->VariableGlobal.VariableServicesLock); - - return Status; -} - /** Register SetVariable check handler. diff --git a/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableLockRequstToLock.c b/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableLockRequstToLock.c new file mode 100644 index 000000000000..1f7f0b7ef06c --- /dev/null +++ b/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableLockRequstToLock.c @@ -0,0 +1,71 @@ +/** @file -- VariableLockRequstToLock.c +Temporary location of the RequestToLock shim code while +projects are moved to VariablePolicy. Should be removed when deprecated. + +Copyright (c) Microsoft Corporation. +SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include <Uefi.h> + +#include <Library/DebugLib.h> +#include <Library/MemoryAllocationLib.h> + +#include <Protocol/VariableLock.h> + +#include <Protocol/VariablePolicy.h> +#include <Library/VariablePolicyLib.h> +#include <Library/VariablePolicyHelperLib.h> + + +/** + DEPRECATED. THIS IS ONLY HERE AS A CONVENIENCE WHILE PORTING. + Mark a variable that will become read-only after leaving the DXE phase of execution. + Write request coming from SMM environment through EFI_SMM_VARIABLE_PROTOCOL is allowed. + + @param[in] This The VARIABLE_LOCK_PROTOCOL instance. + @param[in] VariableName A pointer to the variable name that will be made read-only subsequently. + @param[in] VendorGuid A pointer to the vendor GUID that will be made read-only subsequently. + + @retval EFI_SUCCESS The variable specified by the VariableName and the VendorGuid was marked + as pending to be read-only. + @retval EFI_INVALID_PARAMETER VariableName or VendorGuid is NULL. + Or VariableName is an empty string. + @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 to hold the lock request. +**/ +EFI_STATUS +EFIAPI +VariableLockRequestToLock ( + IN CONST EDKII_VARIABLE_LOCK_PROTOCOL *This, + IN CHAR16 *VariableName, + IN EFI_GUID *VendorGuid + ) +{ + EFI_STATUS Status; + VARIABLE_POLICY_ENTRY *NewPolicy; + + NewPolicy = NULL; + Status = CreateBasicVariablePolicy( VendorGuid, + VariableName, + VARIABLE_POLICY_NO_MIN_SIZE, + VARIABLE_POLICY_NO_MAX_SIZE, + VARIABLE_POLICY_NO_MUST_ATTR, + VARIABLE_POLICY_NO_CANT_ATTR, + VARIABLE_POLICY_TYPE_LOCK_NOW, + &NewPolicy ); + if (!EFI_ERROR( Status )) { + Status = RegisterVariablePolicy( NewPolicy ); + } + if (EFI_ERROR( Status )) { + DEBUG(( DEBUG_ERROR, "%a - Failed to lock variable %s! %r\n", __FUNCTION__, VariableName, Status )); + ASSERT_EFI_ERROR( Status ); + } + if (NewPolicy != NULL) { + FreePool( NewPolicy ); + } + + return Status; +} diff --git a/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableRuntimeDxe.inf b/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableRuntimeDxe.inf index 5fbec5cee8ab..2d1261ef0fba 100644 --- a/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableRuntimeDxe.inf +++ b/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableRuntimeDxe.inf @@ -49,6 +49,7 @@ VarCheck.c VariableExLib.c SpeculationBarrierDxe.c + VariableLockRequstToLock.c [Packages] MdePkg/MdePkg.dec @@ -70,6 +71,7 @@ TpmMeasurementLib AuthVariableLib VarCheckLib + VariablePolicyLib VariablePolicyHelperLib [Protocols] diff --git a/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableSmm.inf b/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableSmm.inf index bbc8d2080193..26fbad97339f 100644 --- a/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableSmm.inf +++ b/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableSmm.inf @@ -58,6 +58,7 @@ VariableExLib.c TcgMorLockSmm.c SpeculationBarrierSmm.c + VariableLockRequstToLock.c [Packages] MdePkg/MdePkg.dec diff --git a/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableStandaloneMm.inf b/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableStandaloneMm.inf index 62f2f9252f43..7c6fdf4d65fd 100644 --- a/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableStandaloneMm.inf +++ b/MdeModulePkg/Universal/Variable/RuntimeDxe/VariableStandaloneMm.inf @@ -58,6 +58,7 @@ VariableExLib.c TcgMorLockSmm.c SpeculationBarrierSmm.c + VariableLockRequstToLock.c [Packages] MdePkg/MdePkg.dec -- 2.16.3.windows.1 ^ permalink raw reply related [flat|nested] 10+ messages in thread
end of thread, other threads:[~2020-04-26 2:03 UTC | newest] Thread overview: 10+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- [not found] <20200410183802.21192-1-michael.kubacki@outlook.com> 2020-04-10 18:37 ` [PATCH v1 2/9] MdeModulePkg: Define the VariablePolicyLib Michael Kubacki 2020-04-22 9:14 ` [edk2-devel] " Guomin Jiang 2020-04-10 18:37 ` [PATCH v1 3/9] MdeModulePkg: Define the VariablePolicyHelperLib Michael Kubacki 2020-04-26 2:03 ` [edk2-devel] " Guomin Jiang 2020-04-10 18:37 ` [PATCH v1 4/9] MdeModulePkg: Define the VarCheckPolicyLib and SMM interface Michael Kubacki 2020-04-10 18:37 ` [PATCH v1 5/9] MdeModulePkg: Connect VariablePolicy business logic to VariableServices Michael Kubacki 2020-04-10 18:37 ` [PATCH v1 6/9] MdeModulePkg: Allow VariablePolicy state to delete protected variables Michael Kubacki 2020-04-10 18:38 ` [PATCH v1 7/9] SecurityPkg: Allow VariablePolicy state to delete authenticated variables Michael Kubacki 2020-04-10 18:38 ` [PATCH v1 8/9] MdeModulePkg: Change TCG MOR variables to use VariablePolicy Michael Kubacki 2020-04-10 18:38 ` [PATCH v1 9/9] MdeModulePkg: Drop VarLock from RuntimeDxe variable driver Michael Kubacki
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox