* [PATCH v7 02/14] MdeModulePkg: Define the VariablePolicyLib
2020-08-28 5:51 [PATCH v7 00/14] Add the VariablePolicy feature Bret Barkelew
2020-08-28 5:51 ` [PATCH v7 01/14] MdeModulePkg: Define the VariablePolicy protocol interface Bret Barkelew
@ 2020-08-28 5:51 ` Bret Barkelew
2020-08-28 5:51 ` [PATCH v7 03/14] MdeModulePkg: Define the VariablePolicyHelperLib Bret Barkelew
` (14 subsequent siblings)
16 siblings, 0 replies; 24+ messages in thread
From: Bret Barkelew @ 2020-08-28 5:51 UTC (permalink / raw)
To: devel; +Cc: Jian J Wang, Hao A Wu, Liming Gao
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>
Cc: Bret Barkelew <brbarkel@microsoft.com>
Signed-off-by: Bret Barkelew <brbarkel@microsoft.com>
---
| 46 +
| 85 +
MdeModulePkg/Library/VariablePolicyLib/VariablePolicyLib.c | 830 +++++++
MdeModulePkg/Library/VariablePolicyLib/VariablePolicyUnitTest/VariablePolicyUnitTest.c | 2452 ++++++++++++++++++++
MdeModulePkg/Include/Library/VariablePolicyLib.h | 207 ++
MdeModulePkg/Library/VariablePolicyLib/ReadMe.md | 410 ++++
MdeModulePkg/Library/VariablePolicyLib/VariablePolicyLib.inf | 49 +
MdeModulePkg/Library/VariablePolicyLib/VariablePolicyLib.uni | 12 +
MdeModulePkg/Library/VariablePolicyLib/VariablePolicyLibRuntimeDxe.inf | 51 +
MdeModulePkg/Library/VariablePolicyLib/VariablePolicyUnitTest/VariablePolicyUnitTest.inf | 45 +
MdeModulePkg/MdeModulePkg.ci.yaml | 4 +-
MdeModulePkg/MdeModulePkg.dec | 3 +
MdeModulePkg/MdeModulePkg.dsc | 5 +
MdeModulePkg/Test/MdeModulePkgHostTest.dsc | 11 +
14 files changed, 4209 insertions(+), 1 deletion(-)
--git a/MdeModulePkg/Library/VariablePolicyLib/VariablePolicyExtraInitNull.c b/MdeModulePkg/Library/VariablePolicyLib/VariablePolicyExtraInitNull.c
new file mode 100644
index 000000000000..ad2ee0b2fb8f
--- /dev/null
+++ b/MdeModulePkg/Library/VariablePolicyLib/VariablePolicyExtraInitNull.c
@@ -0,0 +1,46 @@
+/** @file -- VariablePolicyExtraInitNull.c
+This file contains extra init and deinit routines that don't do anything
+extra.
+
+Copyright (c) Microsoft Corporation.
+SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include <Library/UefiRuntimeServicesTableLib.h>
+
+
+/**
+ An extra init hook that enables the RuntimeDxe library instance to
+ register VirtualAddress change callbacks. Among other things.
+
+ @retval EFI_SUCCESS Everything is good. Continue with init.
+ @retval Others Uh... don't continue.
+
+**/
+EFI_STATUS
+VariablePolicyExtraInit (
+ VOID
+ )
+{
+ // NULL implementation.
+ return EFI_SUCCESS;
+}
+
+
+/**
+ An extra deinit hook that enables the RuntimeDxe library instance to
+ register VirtualAddress change callbacks. Among other things.
+
+ @retval EFI_SUCCESS Everything is good. Continue with deinit.
+ @retval Others Uh... don't continue.
+
+**/
+EFI_STATUS
+VariablePolicyExtraDeinit (
+ VOID
+ )
+{
+ // NULL implementation.
+ return EFI_SUCCESS;
+}
--git a/MdeModulePkg/Library/VariablePolicyLib/VariablePolicyExtraInitRuntimeDxe.c b/MdeModulePkg/Library/VariablePolicyLib/VariablePolicyExtraInitRuntimeDxe.c
new file mode 100644
index 000000000000..3ca87048b14b
--- /dev/null
+++ b/MdeModulePkg/Library/VariablePolicyLib/VariablePolicyExtraInitRuntimeDxe.c
@@ -0,0 +1,85 @@
+/** @file -- VariablePolicyExtraInitRuntimeDxe.c
+This file contains extra init and deinit routines that register and unregister
+VariableAddressChange callbacks.
+
+Copyright (c) Microsoft Corporation.
+SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/UefiRuntimeServicesTableLib.h>
+
+extern EFI_GET_VARIABLE mGetVariableHelper;
+extern UINT8 *mPolicyTable;
+STATIC BOOLEAN mIsVirtualAddrConverted;
+STATIC EFI_EVENT mVariablePolicyLibVirtualAddressChangeEvent = NULL;
+
+/**
+ For the RuntimeDxe version of this lib, convert internal pointer addresses to virtual addresses.
+
+ @param[in] Event Event whose notification function is being invoked.
+ @param[in] Context The pointer to the notification function's context, which
+ is implementation-dependent.
+**/
+STATIC
+VOID
+EFIAPI
+VariablePolicyLibVirtualAddressCallback (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ )
+{
+ gRT->ConvertPointer (0, (VOID **)&mPolicyTable);
+ gRT->ConvertPointer (0, (VOID **)&mGetVariableHelper);
+ mIsVirtualAddrConverted = TRUE;
+}
+
+
+/**
+ An extra init hook that enables the RuntimeDxe library instance to
+ register VirtualAddress change callbacks. Among other things.
+
+ @retval EFI_SUCCESS Everything is good. Continue with init.
+ @retval Others Uh... don't continue.
+
+**/
+EFI_STATUS
+VariablePolicyExtraInit (
+ VOID
+ )
+{
+ return gBS->CreateEventEx (EVT_NOTIFY_SIGNAL,
+ TPL_NOTIFY,
+ VariablePolicyLibVirtualAddressCallback,
+ NULL,
+ &gEfiEventVirtualAddressChangeGuid,
+ &mVariablePolicyLibVirtualAddressChangeEvent);
+}
+
+
+/**
+ An extra deinit hook that enables the RuntimeDxe library instance to
+ register VirtualAddress change callbacks. Among other things.
+
+ @retval EFI_SUCCESS Everything is good. Continue with deinit.
+ @retval Others Uh... don't continue.
+
+**/
+EFI_STATUS
+VariablePolicyExtraDeinit (
+ VOID
+ )
+{
+ EFI_STATUS Status;
+
+ Status = EFI_SUCCESS;
+ if (mIsVirtualAddrConverted) {
+ Status = gBS->CloseEvent (mVariablePolicyLibVirtualAddressChangeEvent);
+ }
+ else {
+ Status = EFI_SUCCESS;
+ }
+
+ return Status;
+}
diff --git a/MdeModulePkg/Library/VariablePolicyLib/VariablePolicyLib.c b/MdeModulePkg/Library/VariablePolicyLib/VariablePolicyLib.c
new file mode 100644
index 000000000000..5029ddb96adb
--- /dev/null
+++ b/MdeModulePkg/Library/VariablePolicyLib/VariablePolicyLib.c
@@ -0,0 +1,830 @@
+/** @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 <Library/DebugLib.h>
+#include <Library/PcdLib.h>
+
+#include <Protocol/VariablePolicy.h>
+#include <Library/VariablePolicyLib.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.
+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.
+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
+
+
+/**
+ An extra init hook that enables the RuntimeDxe library instance to
+ register VirtualAddress change callbacks. Among other things.
+
+ @retval EFI_SUCCESS Everything is good. Continue with init.
+ @retval Others Uh... don't continue.
+
+**/
+EFI_STATUS
+VariablePolicyExtraInit (
+ VOID
+ );
+
+/**
+ An extra deinit hook that enables the RuntimeDxe library instance to
+ register VirtualAddress change callbacks. Among other things.
+
+ @retval EFI_SUCCESS Everything is good. Continue with deinit.
+ @retval Others Uh... don't continue.
+
+**/
+EFI_STATUS
+VariablePolicyExtraDeinit (
+ VOID
+ );
+
+
+/**
+ 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;
+ CHAR16 *PolicyName;
+ UINT8 CalculatedPriority;
+ UINTN Index;
+
+ Result = FALSE;
+ CalculatedPriority = MATCH_PRIORITY_EXACT;
+
+ // 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') ||
+ (L'A' <= VariableName[Index] && VariableName[Index] <= L'F') ||
+ (L'a' <= VariableName[Index] && VariableName[Index] <= L'f'))) {
+ 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;
+ VARIABLE_POLICY_ENTRY *CurrentEntry;
+ UINT8 MatchPriority;
+ UINT8 CurrentPriority;
+ UINTN Index;
+
+ BestResult = NULL;
+ MatchPriority = MATCH_PRIORITY_EXACT;
+
+ // 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 )) {
+ // 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;
+ VARIABLE_LOCK_ON_VAR_STATE_POLICY *StateVarPolicy;
+ CHAR16 *StateVarName;
+ UINTN StateVarSize;
+ UINT8 StateVar;
+
+ ReturnStatus = EFI_SUCCESS;
+
+ if (!IsVariablePolicyLibInitialized()) {
+ ReturnStatus = EFI_NOT_READY;
+ goto Exit;
+ }
+
+ // Bail if the protections are currently disabled.
+ if (mProtectionDisabled) {
+ 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_WRITE_PROTECTED Interface option is disabled by platform PCD.
+ @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;
+ }
+ if (!PcdGetBool (PcdAllowVariablePolicyEnforcementDisable)) {
+ 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
+ )
+{
+ EFI_STATUS Status;
+
+ Status = EFI_SUCCESS;
+
+ if (mGetVariableHelper != NULL) {
+ return EFI_ALREADY_STARTED;
+ }
+
+ if (!EFI_ERROR( Status )) {
+ Status = VariablePolicyExtraInit();
+ }
+
+ if (!EFI_ERROR( Status )) {
+ // 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 Status;
+}
+
+
+/**
+ 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
+ )
+{
+ EFI_STATUS Status;
+
+ Status = EFI_SUCCESS;
+
+ if (mGetVariableHelper == NULL) {
+ return EFI_NOT_READY;
+ }
+
+ if (!EFI_ERROR( Status )) {
+ Status = VariablePolicyExtraDeinit();
+ }
+
+ if (!EFI_ERROR( Status )) {
+ mGetVariableHelper = NULL;
+ mInterfaceLocked = FALSE;
+ mProtectionDisabled = FALSE;
+ mCurrentTableSize = 0;
+ mCurrentTableUsage = 0;
+ mCurrentTableCount = 0;
+
+ if (mPolicyTable != NULL) {
+ FreePool( mPolicyTable );
+ mPolicyTable = NULL;
+ }
+ }
+
+ return Status;
+}
diff --git a/MdeModulePkg/Library/VariablePolicyLib/VariablePolicyUnitTest/VariablePolicyUnitTest.c b/MdeModulePkg/Library/VariablePolicyLib/VariablePolicyUnitTest/VariablePolicyUnitTest.c
new file mode 100644
index 000000000000..40e946a73814
--- /dev/null
+++ b/MdeModulePkg/Library/VariablePolicyLib/VariablePolicyUnitTest/VariablePolicyUnitTest.c
@@ -0,0 +1,2452 @@
+/** @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>
+
+#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)
+#define SIMPLE_VARIABLE_POLICY_ENTRY_VAR_NAME_LENGTH 1001 // 1000 characters + terminator.
+#define SIMPLE_VARIABLE_POLICY_ENTRY_VAR_NAME_SIZE (SIMPLE_VARIABLE_POLICY_ENTRY_VAR_NAME_LENGTH * sizeof(CHAR16))
+typedef struct _SIMPLE_VARIABLE_POLICY_ENTRY {
+ VARIABLE_POLICY_ENTRY Header;
+ CHAR16 Name[SIMPLE_VARIABLE_POLICY_ENTRY_VAR_NAME_LENGTH];
+} 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 ===========================================================================
+
+/**
+ Helper function to initialize a VARIABLE_POLICY_ENTRY structure with a Name and StateName.
+
+ Takes care of all the messy packing.
+
+ @param[in,out] Entry
+ @param[in] Name [Optional]
+ @param[in] StateName [Optional]
+
+ @retval TRUE
+ @retval FALSE
+
+**/
+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;
+}
+
+/**
+ Mocked version of GetVariable, for testing.
+**/
+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 != NULL) {
+ *Attributes = MockedAttr;
+ }
+ if (Data != NULL && !EFI_ERROR(MockedReturn)) {
+ CopyMem( Data, MockedData, MockedDataSize );
+ }
+
+ *DataSize = MockedDataSize;
+
+ return MockedReturn;
+}
+
+//
+// Anything you think might be helpful that isn't a test itself.
+//
+
+/**
+ This is a common setup function that will ensure the library is always initialized
+ with the stubbed GetVariable.
+
+ Not used by all test cases, but by most.
+**/
+STATIC
+UNIT_TEST_STATUS
+EFIAPI
+LibInitMocked (
+ IN UNIT_TEST_CONTEXT Context
+ )
+{
+ return EFI_ERROR(InitVariablePolicyLib( StubGetVariableNull )) ? UNIT_TEST_ERROR_PREREQUISITE_NOT_MET : UNIT_TEST_PASSED;
+}
+
+/**
+ Common cleanup function to make sure that the library is always de-initialized prior
+ to the next test case.
+*/
+STATIC
+VOID
+EFIAPI
+LibCleanup (
+ IN UNIT_TEST_CONTEXT Context
+ )
+{
+ DeinitVariablePolicyLib();
+}
+
+
+///=== TEST CASES =================================================================================
+
+///===== ARCHITECTURAL SUITE ==================================================
+
+/**
+ Test Case
+*/
+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;
+}
+
+/**
+ Test Case
+*/
+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;
+}
+
+/**
+ Test Case
+*/
+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;
+}
+
+/**
+ Test Case
+*/
+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
+ );
+
+/**
+ Test Case
+*/
+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;
+}
+
+/**
+ Test Case
+*/
+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 *CheckVarFName = L"WildcardFVarName0A";
+ CHAR16 *CheckVarZName = L"WildcardZVarName56";
+ CHAR16 *CheckVarLName = L"WildcardLVarName56";
+ CHAR16 *CheckVarHName = L"Wildcard#VarName56";
+
+ // Make sure that all hexidecimal sets of wildcard numbers match.
+ UT_ASSERT_TRUE( EvaluatePolicyMatch( &MatchCheckPolicy.Header, CheckVar1Name, &mTestGuid1, NULL ) );
+ UT_ASSERT_TRUE( EvaluatePolicyMatch( &MatchCheckPolicy.Header, CheckVar2Name, &mTestGuid1, NULL ) );
+ UT_ASSERT_TRUE( EvaluatePolicyMatch( &MatchCheckPolicy.Header, CheckVarBName, &mTestGuid1, NULL ) );
+ UT_ASSERT_TRUE( EvaluatePolicyMatch( &MatchCheckPolicy.Header, CheckVarFName, &mTestGuid1, NULL ) );
+
+ // Make sure that the non-number charaters don't match.
+ UT_ASSERT_FALSE( EvaluatePolicyMatch( &MatchCheckPolicy.Header, CheckVarZName, &mTestGuid1, NULL ) );
+ UT_ASSERT_FALSE( EvaluatePolicyMatch( &MatchCheckPolicy.Header, CheckVarLName, &mTestGuid1, NULL ) );
+
+ // Make sure that '#' signs don't match.
+ UT_ASSERT_FALSE( EvaluatePolicyMatch( &MatchCheckPolicy.Header, CheckVarHName, &mTestGuid1, NULL ) );
+
+ return UNIT_TEST_PASSED;
+}
+
+/**
+ Test Case
+*/
+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 *CheckValidHexString = L"012345678901234567890123456789012345678901234F6789"\
+ "01234567890123456789012345678901234567890123456789"\
+ "012345678901ABC56789012345678901234567890123456789"\
+ "01234567890123456789012345678901234567890123456789"\
+ "012345678901234567890123456789012345678DEADBEEF789"\
+ "01234ABCDEF123456789012345678901234567890123456789";
+ 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_TRUE( EvaluatePolicyMatch( &MatchCheckPolicy.Header, CheckValidHexString, &mTestGuid1, &MatchPriority ) );
+ UT_ASSERT_EQUAL( MatchPriority, MAX_UINT8 );
+
+ return UNIT_TEST_PASSED;
+}
+
+/**
+ Test Case
+*/
+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;
+}
+
+/**
+ Test Case
+*/
+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 ==============================================
+
+/**
+ Test Case
+*/
+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;
+}
+
+/**
+ Test Case
+*/
+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;
+}
+
+/**
+ Test Case
+*/
+UNIT_TEST_STATUS
+EFIAPI
+RegisterShouldRejectNullPointers (
+ IN UNIT_TEST_CONTEXT Context
+ )
+{
+ UT_ASSERT_EQUAL( RegisterVariablePolicy( NULL ), EFI_INVALID_PARAMETER );
+ return UNIT_TEST_PASSED;
+}
+
+/**
+ Test Case
+*/
+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;
+}
+
+/**
+ Test Case
+*/
+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;
+}
+
+/**
+ Test Case
+*/
+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;
+}
+
+/**
+ Test Case
+*/
+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;
+}
+
+/**
+ Test Case
+*/
+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;
+}
+
+/**
+ Test Case
+*/
+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;
+}
+
+/**
+ Test Case
+*/
+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;
+}
+
+/**
+ Test Case
+*/
+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;
+}
+
+/**
+ Test Case
+*/
+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;
+}
+
+/**
+ Test Case
+*/
+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;
+}
+
+/**
+ Test Case
+*/
+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;
+}
+
+/**
+ Test Case
+*/
+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;
+}
+
+/**
+ Test Case
+*/
+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;
+}
+
+/**
+ Test Case
+*/
+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;
+}
+
+/**
+ Test Case
+*/
+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;
+}
+
+/**
+ Test Case
+*/
+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;
+}
+
+/**
+ Test Case
+*/
+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;
+}
+
+/**
+ Test Case
+*/
+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;
+}
+
+/**
+ Test Case
+*/
+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;
+}
+
+/**
+ Test Case
+*/
+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;
+}
+
+/**
+ Test Case
+*/
+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;
+}
+
+/**
+ Test Case
+*/
+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 ===================================================
+
+/**
+ Test Case
+*/
+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;
+}
+
+/**
+ Test Case
+*/
+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;
+}
+
+/**
+ Test Case
+*/
+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;
+}
+
+/**
+ Test Case
+*/
+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;
+}
+
+/**
+ Test Case
+*/
+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;
+}
+
+/**
+ Test Case
+*/
+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;
+}
+
+/**
+ Test Case
+*/
+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 != NULL)
+ {
+ FreeUnitTestFramework( Framework );
+ }
+
+ return Status;
+}
diff --git a/MdeModulePkg/Include/Library/VariablePolicyLib.h b/MdeModulePkg/Include/Library/VariablePolicyLib.h
new file mode 100644
index 000000000000..efd1840112ec
--- /dev/null
+++ b/MdeModulePkg/Include/Library/VariablePolicyLib.h
@@ -0,0 +1,207 @@
+/** @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_WRITE_PROTECTED Interface option is disabled by platform PCD.
+ @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/ReadMe.md b/MdeModulePkg/Library/VariablePolicyLib/ReadMe.md
new file mode 100644
index 000000000000..9e87b2e25f7d
--- /dev/null
+++ b/MdeModulePkg/Library/VariablePolicyLib/ReadMe.md
@@ -0,0 +1,410 @@
+---
+title: UEFI Variable Policy Whitepaper
+version: 1.0
+copyright: Copyright (c) Microsoft Corporation.
+---
+
+# UEFI Variable Policy
+
+## Summary
+
+UEFI Variable Policy spec aims to describe the DXE protocol interface
+which allows enforcing certain rules on certain UEFI variables. The
+protocol allows communication with the Variable Policy Engine which
+performs the policy enforcement.
+
+The Variable Policy is comprised of a set of policy entries which
+describe, per UEFI variable (identified by namespace GUID and variable
+name) the following rules:
+
+- Required variable attributes
+- Prohibited variable attributes
+- Minimum variable size
+- Maximum variable size
+- Locking:
+ - Locking "immediately"
+ - Locking on creation
+ - Locking based on a state of another variable
+
+The spec assumes that the Variable Policy Engine runs in a trusted
+enclave, potentially off the main CPU that runs UEFI. For that reason,
+it is assumed that the Variable Policy Engine has no concept of UEFI
+events, and that the communication from the DXE driver to the trusted
+enclave is proprietary.
+
+At power-on, the Variable Policy Engine is:
+
+- Enabled -- present policy entries are evaluated on variable access
+ calls.
+- Unlocked -- new policy entries can be registered.
+
+Policy is expected to be clear on power-on. Policy is volatile and not
+preserved across system reset.
+
+## DXE Protocol
+
+```h
+typedef struct {
+ UINT64 Revision;
+ DISABLE_VARIABLE_POLICY DisableVariablePolicy;
+ IS_VARIABLE_POLICY_ENABLED IsVariablePolicyEnabled;
+ REGISTER_VARIABLE_POLICY RegisterVariablePolicy;
+ DUMP_VARIABLE_POLICY DumpVariablePolicy;
+ LOCK_VARIABLE_POLICY LockVariablePolicy;
+} _VARIABLE_POLICY_PROTOCOL;
+
+typedef _VARIABLE_POLICY_PROTOCOL VARIABLE_POLICY_PROTOCOL;
+
+extern EFI_GUID gVariablePolicyProtocolGuid;
+```
+
+```text
+## Include/Protocol/VariablePolicy.h
+ gVariablePolicyProtocolGuid = { 0x81D1675C, 0x86F6, 0x48DF, { 0xBD, 0x95, 0x9A, 0x6E, 0x4F, 0x09, 0x25, 0xC3 } }
+```
+
+### DisableVariablePolicy
+
+Function prototype:
+
+```c
+EFI_STATUS
+EFIAPI
+DisableVariablePolicy (
+ VOID
+ );
+```
+
+`DisableVariablePolicy` call disables the Variable Policy Engine, so
+that the present policy entries are no longer taken into account on
+variable access calls. This call effectively turns off the variable
+policy verification for this boot. This also disables UEFI
+Authenticated Variable protections including Secure Boot.
+`DisableVariablePolicy` can only be called once during boot. If called
+more than once, it will return `EFI_ALREADY_STARTED`. Note, this process
+is irreversible until the next system reset -- there is no
+"EnablePolicy" protocol function.
+
+_IMPORTANT NOTE:_ It is strongly recommended that VariablePolicy *NEVER*
+be disabled in "normal, production boot conditions". It is expected to always
+be enforced. The most likely reasons to disable are for Manufacturing and
+Refurbishing scenarios. If in doubt, leave the `gEfiMdeModulePkgTokenSpaceGuid.PcdAllowVariablePolicyEnforcementDisable`
+PCD set to `FALSE` and VariablePolicy will always be enabled.
+
+### IsVariablePolicyEnabled
+
+Function prototype:
+
+```c
+EFI_STATUS
+EFIAPI
+IsVariablePolicyEnabled (
+ OUT BOOLEAN *State
+ );
+```
+
+`IsVariablePolicyEnabled` accepts a pointer to a Boolean in which it
+will store `TRUE` if Variable Policy Engine is enabled, or `FALSE` if
+Variable Policy Engine is disabled. The function returns `EFI_SUCCESS`.
+
+### RegisterVariablePolicy
+
+Function prototype:
+
+```c
+EFI_STATUS
+EFIAPI
+RegisterVariablePolicy (
+ IN CONST VARIABLE_POLICY_ENTRY *PolicyEntry
+ );
+```
+
+`RegisterVariablePolicy` call accepts a pointer to a policy entry
+structure and returns the status of policy registration. If the
+Variable Policy Engine is not locked and the policy structures are
+valid, the function will return `EFI_SUCCESS`. If the Variable Policy
+Engine is locked, `RegisterVariablePolicy` call will return
+`EFI_WRITE_PROTECTED` and will not register the policy entry. Bulk
+registration is not supported at this time due to the requirements
+around error handling on each policy registration.
+
+Upon successful registration of a policy entry, Variable Policy Engine
+will then evaluate this entry on subsequent variable access calls (as
+long as Variable Policy Engine hasn't been disabled).
+
+### DumpVariablePolicy
+
+Function prototype:
+
+```c
+EFI_STATUS
+EFIAPI
+DumpVariablePolicy (
+ OUT UINT8 *Policy,
+ IN OUT UINT32 *Size
+ );
+```
+
+`DumpVariablePolicy` call accepts a pointer to a buffer and a pointer to
+the size of the buffer as parameters and returns the status of placing
+the policy into the buffer. On first call to `DumpVariablePolicy` one
+should pass `NULL` as the buffer and a pointer to 0 as the `Size` variable
+and `DumpVariablePolicy` will return `EFI_BUFFER_TOO_SMALL` and will
+populate the `Size` parameter with the size of the needed buffer to
+store the policy. This way, the caller can allocate the buffer of
+correct size and call `DumpVariablePolicy` again. The function will
+populate the buffer with policy and return `EFI_SUCCESS`.
+
+### LockVariablePolicy
+
+Function prototype:
+
+```c
+EFI_STATUS
+EFIAPI
+LockVariablePolicy (
+ VOID
+ );
+```
+
+`LockVariablePolicy` locks the Variable Policy Engine, i.e. prevents any
+new policy entries from getting registered in this boot
+(`RegisterVariablePolicy` calls will fail with `EFI_WRITE_PROTECTED`
+status code returned).
+
+## Policy Structure
+
+The structure below is meant for the DXE protocol calling interface,
+when communicating to the Variable Policy Engine, thus the pragma pack
+directive. How these policies are stored in memory is up to the
+implementation.
+
+```c
+#pragma pack(1)
+typedef struct {
+ UINT32 Version;
+ UINT16 Size;
+ UINT16 OffsetToName;
+ EFI_GUID Namespace;
+ UINT32 MinSize;
+ UINT32 MaxSize;
+ UINT32 AttributesMustHave;
+ UINT32 AttributesCantHave;
+ UINT8 LockPolicyType;
+ UINT8 Reserved[3];
+ // UINT8 LockPolicy[]; // Variable Length Field
+ // CHAR16 Name[]; // Variable Length Field
+} VARIABLE_POLICY_ENTRY;
+```
+
+The struct `VARIABLE_POLICY_ENTRY` above describes the layout for a policy
+entry. The first element, `Size`, is the size of the policy entry, then
+followed by `OffsetToName` -- the number of bytes from the beginning of
+the struct to the name of the UEFI variable targeted by the policy
+entry. The name can contain wildcards to match more than one variable,
+more on this in the Wildcards section. The rest of the struct elements
+are self-explanatory.
+
+```cpp
+#define VARIABLE_POLICY_TYPE_NO_LOCK 0
+#define VARIABLE_POLICY_TYPE_LOCK_NOW 1
+#define VARIABLE_POLICY_TYPE_LOCK_ON_CREATE 2
+#define VARIABLE_POLICY_TYPE_LOCK_ON_VAR_STATE 3
+```
+
+`LockPolicyType` can have the following values:
+
+- `VARIABLE_POLICY_TYPE_NO_LOCK` -- means that no variable locking is performed. However,
+ the attribute and size constraints are still enforced. LockPolicy
+ field is size 0.
+- `VARIABLE_POLICY_TYPE_LOCK_NOW` -- means that the variable starts being locked
+ immediately after policy entry registration. If the variable doesn't
+ exist at this point, being LockedNow means it cannot be created on
+ this boot. LockPolicy field is size 0.
+- `VARIABLE_POLICY_TYPE_LOCK_ON_CREATE` -- means that the variable starts being locked
+ after it is created. This allows for variable creation and
+ protection after LockVariablePolicy() function has been called. The
+ LockPolicy field is size 0.
+- `VARIABLE_POLICY_TYPE_LOCK_ON_VAR_STATE` -- means that the Variable Policy Engine will
+ examine the state/contents of another variable to determine if the
+ variable referenced in the policy entry is locked.
+
+```c
+typedef struct {
+ EFI_GUID Namespace;
+ UINT8 Value;
+ UINT8 Reserved;
+ // CHAR16 Name[]; // Variable Length Field
+} VARIABLE_LOCK_ON_VAR_STATE_POLICY;
+```
+
+If `LockPolicyType` is `VARIABLE_POLICY_TYPE_LOCK_ON_VAR_STATE`, then the final element in the
+policy entry struct is of type `VARIABLE_LOCK_ON_VAR_STATE_POLICY`, which
+lists the namespace GUID, name (no wildcards here), and value of the
+variable which state determines the locking of the variable referenced
+in the policy entry. The "locking" variable must be 1 byte in terms of
+payload size. If the Referenced variable contents match the Value of the
+`VARIABLE_LOCK_ON_VAR_STATE_POLICY` structure, the lock will be considered
+active and the target variable will be locked. If the Reference variable
+does not exist (ie. returns `EFI_NOT_FOUND`), this policy will be
+considered inactive.
+
+## Variable Name Wildcards
+
+Two types of wildcards can be used in the UEFI variable name field in a
+policy entry:
+
+1. If the Name is a zero-length array (easily checked by comparing
+ fields `Size` and `OffsetToName` -- if they're the same, then the
+ `Name` is zero-length), then all variables in the namespace specified
+ by the provided GUID are targeted by the policy entry.
+2. Character "#" in the `Name` corresponds to one numeric character
+ (0-9, A-F, a-f). For example, string "Boot####" in the `Name`
+ field of the policy entry will make it so that the policy entry will
+ target variables named "Boot0001", "Boot0002", etc.
+
+Given the above two types of wildcards, one variable can be targeted by
+more than one policy entry, thus there is a need to establish the
+precedence rule: a more specific match is applied. When a variable
+access operation is performed, Variable Policy Engine should first check
+the variable being accessed against the policy entries without
+wildcards, then with 1 wildcard, then with 2 wildcards, etc., followed
+in the end by policy entries that match the whole namespace. One can
+still imagine a situation where two policy entries with the same number
+of wildcards match the same variable -- for example, policy entries with
+Names "Boot00##" and "Boot##01" will both match variable "Boot0001".
+Such situation can (and should) be avoided by designing mutually
+exclusive Name strings with wildcards, however, if it occurs, then the
+policy entry that was registered first will be used. After the most
+specific match is selected, all other policies are ignored.
+
+## Available Testing
+
+This functionality is current supported by two kinds of tests: there is a host-based
+unit test for the core business logic (this test accompanies the `VariablePolicyLib`
+implementation that lives in `MdeModulePkg/Library`) and there is a functional test
+for the protocol and its interfaces (this test lives in the `MdeModulePkg/Test/ShellTest`
+directory).
+
+### Host-Based Unit Test
+
+This test:
+
+`MdeModulePkg\Library\VariablePolicyLib\VariablePolicyUnitTest\VariablePolicyUnitTest.inf`
+
+can be run as part of the Host-Based Unit Testing infrastructure provided by EDK2
+PyTools (documented elsewhere). It will test all internal guarantees and is
+where you will find test cases for most of the policy matching and security of the
+Variable Policy Engine.
+
+### Shell-Based Functional Test
+
+This test -- [Variable Policy Functional Unit Test](https://github.com/microsoft/mu_plus/tree/release/202005/UefiTestingPkg/FunctionalSystemTests/VarPolicyUnitTestApp) -- can be built as a
+UEFI Shell application and run to validate that the Variable Policy Engine
+is correctly installed and enforcing policies on the target system.
+
+NOTE: This test _must_ be run prior to calling `DisableVariablePolicy` for all
+test cases to pass. For this reason, it is recommended to run this on a test-built
+FW for complete results, and then again on a production-built FW for release
+results.
+
+## Use Cases
+
+The below examples are hypothetical scenarios based on real-world requirements
+that demonstrate how Variable Policies could be constructed to solve various
+problems.
+
+### UEFI Setup Variables (Example 1)
+
+Variables containing values of the setup options exposed via UEFI
+menu (setup variables). These would be locked based on a state of
+another variable, "ReadyToBoot", which would be set to 1 at the
+ReadyToBoot event. Thus, the policy for the setup variables would be
+of type `LockOnVarState`, with the "ReadyToBoot" listed as the name of
+the variable, appropriate GUID listed as the namespace, and 1 as
+value. Entry into the trusted UEFI menu app doesn't signal
+ReadyToBoot, but booting to any device does, and the setup variables
+are write-protected. The "ReadyToBoot" variable would need to be
+locked-on-create. *(THIS IS ESSENTIALLY LOCK ON EVENT, BUT SINCE THE
+POLICY ENGINE IS NOT IN THE UEFI ENVIRONMENT VARIABLES ARE USED)*
+
+For example, "AllowPXEBoot" variable locked by "ReadyToBoot" variable.
+
+(NOTE: In the below example, the emphasized fields ('Namespace', 'Value', and 'Name')
+are members of the `VARIABLE_LOCK_ON_VAR_STATE_POLICY` structure.)
+
+Size | ...
+---- | ---
+OffsetToName | ...
+NameSpace | ...
+MinSize | ...
+MaxSize | ...
+AttributesMustHave | ...
+AttributesCantHave | ...
+LockPolicyType | `VARIABLE_POLICY_TYPE_LOCK_ON_VAR_STATE`
+_Namespace_ | ...
+_Value_ | 1
+_Name_ | "ReadyToBoot"
+//Name | "AllowPXEBoot"
+
+### Manufacturing VPD (Example 2)
+
+Manufacturing Variable Provisioning Data (VPD) is stored in
+variables and is created while in Manufacturing (MFG) Mode. In MFG
+Mode Variable Policy Engine is disabled, thus these VPD variables
+can be created. These variables are locked with lock policy type
+`LockNow`, so that these variables can't be tampered with in Customer
+Mode. To overwrite or clear VPD, the device would need to MFG mode,
+which is standard practice for refurbishing/remanufacturing
+scenarios.
+
+Example: "DisplayPanelCalibration" variable...
+
+Size | ...
+---- | ---
+OffsetToName | ...
+NameSpace | ...
+MinSize | ...
+MaxSize | ...
+AttributesMustHave | ...
+AttributesCantHave | ...
+LockPolicyType | `VARIABLE_POLICY_TYPE_LOCK_NOW`
+// Name | "DisplayPanelCalibration"
+
+### 3rd Party Calibration Data (Example 3)
+
+Bluetooth pre-pairing variables are locked-on-create because these
+get created by an OS application when Variable Policy is in effect.
+
+Example: "KeyboardBTPairing" variable
+
+Size | ...
+---- | ---
+OffsetToName | ...
+NameSpace | ...
+MinSize | ...
+MaxSize | ...
+AttributesMustHave | ...
+AttributesCantHave | ...
+LockPolicyType | `VARIABLE_POLICY_TYPE_LOCK_ON_CREATE`
+// Name | "KeyboardBTPairing"
+
+### Software-based Variable Policy (Example 4)
+
+Example: "Boot####" variables (a name string with wildcards that
+will match variables "Boot0000" to "BootFFFF") locked by "LockBootOrder"
+variable.
+
+Size | ...
+---- | ---
+OffsetToName | ...
+NameSpace | ...
+MinSize | ...
+MaxSize | ...
+AttributesMustHave | ...
+AttributesCantHave | ...
+LockPolicyType | `VARIABLE_POLICY_TYPE_LOCK_ON_VAR_STATE`
+_Namespace_ | ...
+_Value_ | 1
+_Name_ | "LockBootOrder"
+//Name | "Boot####"
diff --git a/MdeModulePkg/Library/VariablePolicyLib/VariablePolicyLib.inf b/MdeModulePkg/Library/VariablePolicyLib/VariablePolicyLib.inf
new file mode 100644
index 000000000000..f35826a4b9d7
--- /dev/null
+++ b/MdeModulePkg/Library/VariablePolicyLib/VariablePolicyLib.inf
@@ -0,0 +1,49 @@
+## @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 = DXE_DRIVER
+ LIBRARY_CLASS = VariablePolicyLib|DXE_DRIVER DXE_SMM_DRIVER MM_STANDALONE
+
+#
+# The following information is for reference only and not required by the build tools.
+#
+# VALID_ARCHITECTURES = ANY
+#
+
+
+[Sources]
+ VariablePolicyLib.c
+ VariablePolicyExtraInitNull.c
+
+
+[Packages]
+ MdePkg/MdePkg.dec
+ MdeModulePkg/MdeModulePkg.dec
+
+
+[LibraryClasses]
+ DebugLib
+ BaseMemoryLib
+ MemoryAllocationLib
+ SafeIntLib
+ PcdLib
+
+
+[Pcd]
+ gEfiMdeModulePkgTokenSpaceGuid.PcdAllowVariablePolicyEnforcementDisable ## CONSUMES
+
+
+[BuildOptions]
+ MSFT:NOOPT_*_*_CC_FLAGS = -DINTERNAL_UNIT_TEST
+ GCC:NOOPT_*_*_CC_FLAGS = -DINTERNAL_UNIT_TEST
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/VariablePolicyLibRuntimeDxe.inf b/MdeModulePkg/Library/VariablePolicyLib/VariablePolicyLibRuntimeDxe.inf
new file mode 100644
index 000000000000..8b8365741864
--- /dev/null
+++ b/MdeModulePkg/Library/VariablePolicyLib/VariablePolicyLibRuntimeDxe.inf
@@ -0,0 +1,51 @@
+## @file VariablePolicyLibRuntimeDxe.inf
+# Business logic for Variable Policy enforcement.
+# This instance is specifically for RuntimeDxe and contains
+# extra routines to register for VirtualAddressChangeEvents.
+#
+# Copyright (c) Microsoft Corporation.
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+##
+
+
+[Defines]
+ INF_VERSION = 0x00010017
+ BASE_NAME = VariablePolicyLibRuntimeDxe
+ FILE_GUID = 205F7F0E-8EAC-4914-8390-1B90DD7E2A27
+ VERSION_STRING = 1.0
+ MODULE_TYPE = DXE_RUNTIME_DRIVER
+ LIBRARY_CLASS = VariablePolicyLib|DXE_RUNTIME_DRIVER
+
+#
+# The following information is for reference only and not required by the build tools.
+#
+# VALID_ARCHITECTURES = ANY
+#
+
+
+[Sources]
+ VariablePolicyLib.c
+ VariablePolicyExtraInitRuntimeDxe.c
+
+
+[Packages]
+ MdePkg/MdePkg.dec
+ MdeModulePkg/MdeModulePkg.dec
+
+
+[LibraryClasses]
+ DebugLib
+ BaseMemoryLib
+ MemoryAllocationLib
+ SafeIntLib
+ UefiBootServicesTableLib
+ UefiRuntimeServicesTableLib
+ PcdLib
+
+
+[Pcd]
+ gEfiMdeModulePkgTokenSpaceGuid.PcdAllowVariablePolicyEnforcementDisable ## CONSUMES
+
+
+[Guids]
+ gEfiEventVirtualAddressChangeGuid
diff --git a/MdeModulePkg/Library/VariablePolicyLib/VariablePolicyUnitTest/VariablePolicyUnitTest.inf b/MdeModulePkg/Library/VariablePolicyLib/VariablePolicyUnitTest/VariablePolicyUnitTest.inf
new file mode 100644
index 000000000000..06489f21d604
--- /dev/null
+++ b/MdeModulePkg/Library/VariablePolicyLib/VariablePolicyUnitTest/VariablePolicyUnitTest.inf
@@ -0,0 +1,45 @@
+## @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
+
+
+[BuildOptions]
+ MSFT:NOOPT_*_*_CC_FLAGS = -DINTERNAL_UNIT_TEST
+ GCC:NOOPT_*_*_CC_FLAGS = -DINTERNAL_UNIT_TEST
diff --git a/MdeModulePkg/MdeModulePkg.ci.yaml b/MdeModulePkg/MdeModulePkg.ci.yaml
index 1a7e955185d8..20d53fc5a5fa 100644
--- a/MdeModulePkg/MdeModulePkg.ci.yaml
+++ b/MdeModulePkg/MdeModulePkg.ci.yaml
@@ -104,7 +104,9 @@
"FVMAIN",
"VARCHECKPCD",
"Getxx",
- "lzturbo"
+ "lzturbo",
+ "musthave",
+ "canthave"
],
"AdditionalIncludePaths": [] # Additional paths to spell check relative to package root (wildcards supported)
}
diff --git a/MdeModulePkg/MdeModulePkg.dec b/MdeModulePkg/MdeModulePkg.dec
index 82aecc40d9a9..51c7057bfd1b 100644
--- a/MdeModulePkg/MdeModulePkg.dec
+++ b/MdeModulePkg/MdeModulePkg.dec
@@ -31,6 +31,9 @@ [LibraryClasses]
## @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 25aea3e2a481..14b6ed536962 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 @@ [LibraryClasses]
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
#
@@ -129,6 +131,7 @@ [LibraryClasses.common.DXE_RUNTIME_DRIVER]
DebugLib|MdePkg/Library/UefiDebugLibConOut/UefiDebugLibConOut.inf
LockBoxLib|MdeModulePkg/Library/SmmLockBoxLib/SmmLockBoxDxeLib.inf
CapsuleLib|MdeModulePkg/Library/DxeCapsuleLibFmp/DxeRuntimeCapsuleLib.inf
+ VariablePolicyLib|MdeModulePkg/Library/VariablePolicyLib/VariablePolicyLibRuntimeDxe.inf
[LibraryClasses.common.SMM_CORE]
HobLib|MdePkg/Library/DxeHobLib/DxeHobLib.inf
@@ -306,6 +309,8 @@ [Components]
MdeModulePkg/Library/BootLogoLib/BootLogoLib.inf
MdeModulePkg/Library/TpmMeasurementLibNull/TpmMeasurementLibNull.inf
MdeModulePkg/Library/AuthVariableLibNull/AuthVariableLibNull.inf
+ MdeModulePkg/Library/VariablePolicyLib/VariablePolicyLib.inf
+ MdeModulePkg/Library/VariablePolicyLib/VariablePolicyLibRuntimeDxe.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..095e613f1be1 100644
--- a/MdeModulePkg/Test/MdeModulePkgHostTest.dsc
+++ b/MdeModulePkg/Test/MdeModulePkgHostTest.dsc
@@ -19,12 +19,23 @@ [Defines]
!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
+
+ <PcdsFixedAtBuild>
+ gEfiMdeModulePkgTokenSpaceGuid.PcdAllowVariablePolicyEnforcementDisable|TRUE
+ }
+
MdeModulePkg/Library/DxeResetSystemLib/UnitTest/DxeResetSystemLibUnitTestHost.inf {
<LibraryClasses>
ResetSystemLib|MdeModulePkg/Library/DxeResetSystemLib/DxeResetSystemLib.inf
--
2.28.0.windows.1
^ permalink raw reply related [flat|nested] 24+ messages in thread
* [PATCH v7 14/14] MdeModulePkg: Add a shell-based functional test for VariablePolicy
2020-08-28 5:51 [PATCH v7 00/14] Add the VariablePolicy feature Bret Barkelew
` (12 preceding siblings ...)
2020-08-28 5:51 ` [PATCH v7 13/14] MdeModulePkg: Drop VarLock from RuntimeDxe variable driver Bret Barkelew
@ 2020-08-28 5:51 ` Bret Barkelew
2020-09-08 1:09 ` 回复: [edk2-devel] [PATCH v7 00/14] Add the VariablePolicy feature gaoliming
` (2 subsequent siblings)
16 siblings, 0 replies; 24+ messages in thread
From: Bret Barkelew @ 2020-08-28 5:51 UTC (permalink / raw)
To: devel; +Cc: Jian J Wang, Hao A Wu, Liming Gao
https://bugzilla.tianocore.org/show_bug.cgi?id=2522
To verify that VariablePolicy is correctly integrated
on platforms, add a Shell-based functional test to
confirm expected behavior.
NOTE: This test assumes that VariablePolicy is built
with PcdAllowVariablePolicyEnforcementDisable set to
TRUE.
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>
Cc: Bret Barkelew <brbarkel@microsoft.com>
Signed-off-by: Bret Barkelew <brbarkel@microsoft.com>
---
MdeModulePkg/Test/ShellTest/VariablePolicyFuncTestApp/VariablePolicyFuncTestApp.c | 2226 ++++++++++++++++++++
MdeModulePkg/MdeModulePkg.ci.yaml | 4 +-
MdeModulePkg/Test/ShellTest/VariablePolicyFuncTestApp/Readme.md | 55 +
MdeModulePkg/Test/ShellTest/VariablePolicyFuncTestApp/VariablePolicyFuncTestApp.inf | 47 +
MdeModulePkg/Test/ShellTest/VariablePolicyFuncTestApp/VariablePolicyTestAuthVar.h | 128 ++
5 files changed, 2459 insertions(+), 1 deletion(-)
diff --git a/MdeModulePkg/Test/ShellTest/VariablePolicyFuncTestApp/VariablePolicyFuncTestApp.c b/MdeModulePkg/Test/ShellTest/VariablePolicyFuncTestApp/VariablePolicyFuncTestApp.c
new file mode 100644
index 000000000000..c2b28e4b642b
--- /dev/null
+++ b/MdeModulePkg/Test/ShellTest/VariablePolicyFuncTestApp/VariablePolicyFuncTestApp.c
@@ -0,0 +1,2226 @@
+/** @file
+UEFI Shell based application for unit testing the Variable Policy Protocol.
+
+Copyright (c) Microsoft Corporation.
+SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#include <Uefi.h>
+#include <Library/UefiLib.h>
+#include <Library/PrintLib.h>
+#include <Library/DebugLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/UnitTestLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Protocol/VariablePolicy.h>
+#include <Library/VariablePolicyHelperLib.h>
+#include <Library/UefiRuntimeServicesTableLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/MemoryAllocationLib.h>
+
+#include "VariablePolicyTestAuthVar.h"
+
+// TODO: Need to add to the UnitTestFrameworkPkg
+// #include <Library/UnitTestBootLib.h>
+
+#define UNIT_TEST_APP_NAME "Variable Policy Unit Test Application"
+#define UNIT_TEST_APP_VERSION "0.1"
+
+// TODO: Need to add to the UnitTestFrameworkPkg
+UNIT_TEST_FRAMEWORK_HANDLE
+GetActiveFrameworkHandle (
+ VOID
+ );
+
+EDKII_VARIABLE_POLICY_PROTOCOL *mVarPol = NULL;
+
+
+EFI_GUID mTestNamespaceGuid1 = { 0x3b389299, 0xabaf, 0x433b, { 0xa4, 0xa9, 0x23, 0xc8, 0x44, 0x02, 0xfc, 0xad } };
+EFI_GUID mTestNamespaceGuid2 = { 0x4c49a3aa, 0xbcb0, 0x544c, { 0xb5, 0xba, 0x34, 0xd9, 0x55, 0x13, 0x0d, 0xbe } };
+EFI_GUID mTestNamespaceGuid3 = { 0x5d5ab4bb, 0xcdc1, 0x655d, { 0xc6, 0xcb, 0x45, 0xea, 0x66, 0x24, 0x1e, 0xcf } };
+
+#define TEST_AUTH_VAR_NAME L"DummyAuthVar"
+EFI_GUID mTestAuthNamespaceGuid = { 0xb6c5a2c6, 0x3ece, 0x4b9b, { 0x8c, 0xc8, 0x96, 0xd8, 0xd9, 0xca, 0xd3, 0x4e } };
+
+/**
+ Prerequisite for most test cases.
+**/
+UNIT_TEST_STATUS
+EFIAPI
+LocateVarPolicyPreReq (
+ IN UNIT_TEST_CONTEXT Context
+ )
+{
+ EFI_STATUS Status;
+
+ if (mVarPol == NULL) {
+ Status = gBS->LocateProtocol (&gEdkiiVariablePolicyProtocolGuid,
+ NULL,
+ (VOID **) &mVarPol);
+ UT_ASSERT_NOT_EFI_ERROR (Status);
+ UT_ASSERT_NOT_NULL (mVarPol);
+ }
+
+ return UNIT_TEST_PASSED;
+} // LocateVarPolicyPreReq
+
+UNIT_TEST_STATUS
+EFIAPI
+VarPolicyEnabledPreReq (
+ IN UNIT_TEST_CONTEXT Context
+ )
+{
+ EFI_STATUS Status;
+ BOOLEAN State;
+
+ UT_ASSERT_EQUAL(LocateVarPolicyPreReq(Context), UNIT_TEST_PASSED);
+ Status = mVarPol->IsVariablePolicyEnabled (&State);
+ UT_ASSERT_NOT_EFI_ERROR(Status);
+ UT_ASSERT_TRUE(State);
+
+ return UNIT_TEST_PASSED;
+}
+
+UNIT_TEST_STATUS
+EFIAPI
+VarPolicyDisabledPreReq (
+ IN UNIT_TEST_CONTEXT Context
+ )
+{
+ EFI_STATUS Status;
+ BOOLEAN State;
+
+ UT_ASSERT_EQUAL(LocateVarPolicyPreReq(Context), UNIT_TEST_PASSED);
+ Status = mVarPol->IsVariablePolicyEnabled (&State);
+ UT_ASSERT_NOT_EFI_ERROR(Status);
+ UT_ASSERT_FALSE(State);
+
+ return UNIT_TEST_PASSED;
+}
+
+/**
+ Getting Started tests.
+**/
+UNIT_TEST_STATUS
+EFIAPI
+CheckVpEnabled (
+ IN UNIT_TEST_CONTEXT Context
+ )
+{
+ EFI_STATUS Status;
+ BOOLEAN State;
+
+ Status = mVarPol->IsVariablePolicyEnabled (&State);
+
+ UT_ASSERT_NOT_EFI_ERROR (Status);
+ UT_ASSERT_EQUAL (State, TRUE);
+
+ return UNIT_TEST_PASSED;
+} // CheckVpEnabled
+
+UNIT_TEST_STATUS
+EFIAPI
+CheckVpRevision (
+ IN UNIT_TEST_CONTEXT Context
+ )
+{
+ UT_ASSERT_NOT_EQUAL (mVarPol->Revision, 0);
+ UT_LOG_INFO ("VP Revision: 0x%x\n", mVarPol->Revision);
+
+ return UNIT_TEST_PASSED;
+} // CheckVpRevision
+
+/**
+ NOTE: Copied from SecureBootConfigImpl.c, then modified.
+
+ Create a time based data payload by concatenating the EFI_VARIABLE_AUTHENTICATION_2
+ descriptor with the input data. NO authentication is required in this function.
+
+ @param[in, out] DataSize On input, the size of Data buffer in bytes.
+ On output, the size of data returned in Data
+ buffer in bytes.
+ @param[in, out] Data On input, Pointer to data buffer to be wrapped or
+ pointer to NULL to wrap an empty payload.
+ On output, Pointer to the new payload date buffer allocated from pool,
+ it's caller's responsibility to free the memory when finish using it.
+ @param[in] Time [Optional] If provided, will be used as the timestamp for the payload.
+ If NULL, a new timestamp will be generated using GetTime().
+
+ @retval EFI_SUCCESS Create time based payload successfully.
+ @retval EFI_OUT_OF_RESOURCES There are not enough memory resources to create time based payload.
+ @retval EFI_INVALID_PARAMETER The parameter is invalid.
+ @retval Others Unexpected error happens.
+
+**/
+STATIC
+EFI_STATUS
+CreateEmptyTimeBasedPayload (
+ IN OUT UINTN *DataSize,
+ IN OUT UINT8 **Data,
+ IN EFI_TIME *Time OPTIONAL
+ )
+{
+ UINT8 *NewData;
+ UINT8 *Payload;
+ UINTN PayloadSize;
+ EFI_VARIABLE_AUTHENTICATION_2 *DescriptorData;
+ UINTN DescriptorSize;
+ EFI_TIME NewTime;
+
+ if (Data == NULL || DataSize == NULL) {
+ DEBUG((DEBUG_ERROR, "CreateEmptyTimeBasedPayload(), invalid arg\n"));
+ return EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // In Setup mode or Custom mode, the variable does not need to be signed but the
+ // parameters to the SetVariable() call still need to be prepared as authenticated
+ // variable. So we create EFI_VARIABLE_AUTHENTICATED_2 descriptor without certificate
+ // data in it.
+ //
+ Payload = *Data;
+ PayloadSize = *DataSize;
+
+ DescriptorSize = OFFSET_OF (EFI_VARIABLE_AUTHENTICATION_2, AuthInfo) + OFFSET_OF (WIN_CERTIFICATE_UEFI_GUID, CertData);
+ NewData = (UINT8*) AllocateZeroPool (DescriptorSize + PayloadSize);
+ if (NewData == NULL) {
+ DEBUG((DEBUG_ERROR, "CreateEmptyTimeBasedPayload() Out of resources.\n"));
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ if ((Payload != NULL) && (PayloadSize != 0)) {
+ CopyMem (NewData + DescriptorSize, Payload, PayloadSize);
+ }
+
+ DescriptorData = (EFI_VARIABLE_AUTHENTICATION_2 *) (NewData);
+
+ //
+ // Use or create the timestamp.
+ //
+ // If Time is NULL, create a new timestamp.
+ if (Time == NULL)
+ {
+ NewTime.Year = 9999;
+ NewTime.Month = 12;
+ NewTime.Day = 31;
+ NewTime.Hour = 23;
+ NewTime.Minute = 59;
+ NewTime.Second = 59;
+ NewTime.Pad1 = 0;
+ NewTime.Nanosecond = 0;
+ NewTime.TimeZone = 0;
+ NewTime.Daylight = 0;
+ NewTime.Pad2 = 0;
+ Time = &NewTime; // Use the new timestamp.
+ }
+ CopyMem (&DescriptorData->TimeStamp, Time, sizeof (EFI_TIME));
+
+ DescriptorData->AuthInfo.Hdr.dwLength = OFFSET_OF (WIN_CERTIFICATE_UEFI_GUID, CertData);
+ DescriptorData->AuthInfo.Hdr.wRevision = 0x0200;
+ DescriptorData->AuthInfo.Hdr.wCertificateType = WIN_CERT_TYPE_EFI_GUID;
+ CopyGuid (&DescriptorData->AuthInfo.CertType, &gEfiCertPkcs7Guid);
+
+ if (Payload != NULL) {
+ FreePool(Payload);
+ }
+
+ *DataSize = DescriptorSize + PayloadSize;
+ *Data = NewData;
+ return EFI_SUCCESS;
+}
+
+/**
+ NoLock Policy tests.
+**/
+UNIT_TEST_STATUS
+EFIAPI
+TestMinSizeNoLock (
+ IN UNIT_TEST_CONTEXT Context
+ )
+{
+ EFI_STATUS Status;
+ UINT8 Value1;
+ UINT32 Value2;
+ UINT8 *Buffer;
+
+ Status = RegisterBasicVariablePolicy (mVarPol,
+ &mTestNamespaceGuid1,
+ L"MinSizeNoLockVar",
+ 4,
+ VARIABLE_POLICY_NO_MAX_SIZE,
+ VARIABLE_POLICY_NO_MUST_ATTR,
+ VARIABLE_POLICY_NO_CANT_ATTR,
+ VARIABLE_POLICY_TYPE_NO_LOCK);
+ UT_ASSERT_NOT_EFI_ERROR (Status);
+
+ //
+ // Try to write a var that is smaller than minsize
+ //
+ Value1 = 0x12;
+ Status = gRT->SetVariable (L"MinSizeNoLockVar",
+ &mTestNamespaceGuid1,
+ (EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS),
+ sizeof (Value1),
+ &Value1);
+ UT_ASSERT_TRUE ((Status == EFI_WRITE_PROTECTED) || (Status == EFI_INVALID_PARAMETER));
+
+ //
+ // Try to write a var of size that matches minsize
+ //
+ Value2 = 0xa1b2c3d4;
+ Status = gRT->SetVariable (L"MinSizeNoLockVar",
+ &mTestNamespaceGuid1,
+ (EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS),
+ sizeof (Value2),
+ &Value2);
+ UT_ASSERT_NOT_EFI_ERROR (Status);
+
+ //
+ // Should be able to delete the var
+ //
+ Status = gRT->SetVariable (L"MinSizeNoLockVar",
+ &mTestNamespaceGuid1,
+ 0,
+ 0,
+ NULL);
+ UT_ASSERT_NOT_EFI_ERROR (Status);
+
+ //
+ // Try to write a var of size larger than minsize
+ //
+ Buffer = AllocateZeroPool (40);
+ UT_ASSERT_NOT_NULL (Buffer);
+ Status = gRT->SetVariable (L"MinSizeNoLockVar",
+ &mTestNamespaceGuid1,
+ (EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS),
+ 40,
+ Buffer);
+ UT_ASSERT_NOT_EFI_ERROR (Status);
+
+ //
+ // Delete the variable
+ //
+ Status = gRT->SetVariable (L"MinSizeNoLockVar",
+ &mTestNamespaceGuid1,
+ 0,
+ 0,
+ NULL);
+ UT_ASSERT_NOT_EFI_ERROR (Status);
+
+ FreePool (Buffer);
+
+ return UNIT_TEST_PASSED;
+} // TestMinSizeNoLock
+
+UNIT_TEST_STATUS
+EFIAPI
+TestMaxSizeNoLock (
+ IN UNIT_TEST_CONTEXT Context
+ )
+{
+ EFI_STATUS Status;
+ UINT8 Value1;
+ UINT32 Value2;
+ UINT8 *Buffer;
+
+ Status = RegisterBasicVariablePolicy (mVarPol,
+ &mTestNamespaceGuid1,
+ L"MaxSizeNoLockVar",
+ VARIABLE_POLICY_NO_MIN_SIZE,
+ 4,
+ VARIABLE_POLICY_NO_MUST_ATTR,
+ VARIABLE_POLICY_NO_CANT_ATTR,
+ VARIABLE_POLICY_TYPE_NO_LOCK);
+ UT_ASSERT_NOT_EFI_ERROR (Status);
+
+ //
+ // Try to write a var that is smaller than maxsize
+ //
+ Value1 = 0x34;
+ Status = gRT->SetVariable (L"MaxSizeNoLockVar",
+ &mTestNamespaceGuid1,
+ (EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS),
+ sizeof (Value1),
+ &Value1);
+ UT_ASSERT_NOT_EFI_ERROR (Status);
+
+ //
+ // Should be able to delete the var
+ //
+ Status = gRT->SetVariable (L"MaxSizeNoLockVar",
+ &mTestNamespaceGuid1,
+ 0,
+ 0,
+ NULL);
+ UT_ASSERT_NOT_EFI_ERROR (Status);
+
+ //
+ // Try to write a var of size that matches maxsize
+ //
+ Value2 = 0xa1b2c3d4;
+ Status = gRT->SetVariable (L"MaxSizeNoLockVar",
+ &mTestNamespaceGuid1,
+ (EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS),
+ sizeof (Value2),
+ &Value2);
+ UT_ASSERT_NOT_EFI_ERROR (Status);
+
+ //
+ // Should be able to delete the var
+ //
+ Status = gRT->SetVariable (L"MaxSizeNoLockVar",
+ &mTestNamespaceGuid1,
+ 0,
+ 0,
+ NULL);
+ UT_ASSERT_NOT_EFI_ERROR (Status);
+
+ //
+ // Try to write a var of size larger than maxsize
+ //
+ Buffer = AllocateZeroPool (40);
+ UT_ASSERT_NOT_NULL (Buffer);
+ Status = gRT->SetVariable (L"MaxSizeNoLockVar",
+ &mTestNamespaceGuid1,
+ (EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS),
+ 40,
+ Buffer);
+ UT_ASSERT_TRUE ((Status == EFI_WRITE_PROTECTED) || (Status == EFI_INVALID_PARAMETER));
+
+ FreePool (Buffer);
+
+ return UNIT_TEST_PASSED;
+} // TestMaxSizeNoLock
+
+UNIT_TEST_STATUS
+EFIAPI
+TestMustHaveAttrNoLock (
+ IN UNIT_TEST_CONTEXT Context
+ )
+{
+ EFI_STATUS Status;
+ UINT8 Value;
+
+ Status = RegisterBasicVariablePolicy (mVarPol,
+ &mTestNamespaceGuid1,
+ L"MustHaveAttrNoLockVar",
+ VARIABLE_POLICY_NO_MIN_SIZE,
+ VARIABLE_POLICY_NO_MAX_SIZE,
+ (EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS),
+ VARIABLE_POLICY_NO_CANT_ATTR,
+ VARIABLE_POLICY_TYPE_NO_LOCK);
+ UT_ASSERT_NOT_EFI_ERROR (Status);
+
+ //
+ // Try to write a var that doesn't have the must-have attributes
+ //
+ Value = 0x56;
+ Status = gRT->SetVariable (L"MustHaveAttrNoLockVar",
+ &mTestNamespaceGuid1,
+ EFI_VARIABLE_BOOTSERVICE_ACCESS,
+ sizeof (Value),
+ &Value);
+ UT_ASSERT_TRUE ((Status == EFI_WRITE_PROTECTED) || (Status == EFI_INVALID_PARAMETER));
+
+ //
+ // Try to write a var that has exactly the required attributes
+ //
+ Status = gRT->SetVariable (L"MustHaveAttrNoLockVar",
+ &mTestNamespaceGuid1,
+ (EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS),
+ sizeof (Value),
+ &Value);
+ UT_ASSERT_NOT_EFI_ERROR (Status);
+
+ //
+ // Should be able to delete the var
+ // NOTE: some implementations of VP will require the musthave attributes to be passed even when deleting
+ //
+ Status = gRT->SetVariable (L"MustHaveAttrNoLockVar",
+ &mTestNamespaceGuid1,
+ 0,
+ 0,
+ NULL);
+ UT_ASSERT_NOT_EFI_ERROR (Status);
+
+ //
+ // Try to write a var that has the required attributes and one extra attribute
+ //
+ Status = gRT->SetVariable (L"MustHaveAttrNoLockVar",
+ &mTestNamespaceGuid1,
+ (EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS),
+ sizeof (Value),
+ &Value);
+ UT_ASSERT_NOT_EFI_ERROR (Status);
+
+ //
+ // Should be able to delete the var
+ // NOTE: some implementations of VP will require the musthave attributes to be passed even when deleting
+ //
+ Status = gRT->SetVariable (L"MustHaveAttrNoLockVar",
+ &mTestNamespaceGuid1,
+ 0,
+ 0,
+ NULL);
+ UT_ASSERT_NOT_EFI_ERROR (Status);
+ return UNIT_TEST_PASSED;
+} // TestMustHaveAttrNoLock
+
+UNIT_TEST_STATUS
+EFIAPI
+TestCantHaveAttrNoLock (
+ IN UNIT_TEST_CONTEXT Context
+ )
+{
+ EFI_STATUS Status;
+ UINT8 Value;
+
+ Status = RegisterBasicVariablePolicy (mVarPol,
+ &mTestNamespaceGuid1,
+ L"CantHaveAttrNoLockVar",
+ VARIABLE_POLICY_NO_MIN_SIZE,
+ VARIABLE_POLICY_NO_MAX_SIZE,
+ VARIABLE_POLICY_NO_MUST_ATTR,
+ EFI_VARIABLE_NON_VOLATILE,
+ VARIABLE_POLICY_TYPE_NO_LOCK);
+ UT_ASSERT_NOT_EFI_ERROR (Status);
+
+ //
+ // Try to write a var that has a can't have attr
+ //
+ Value = 0x78;
+ Status = gRT->SetVariable (L"CantHaveAttrNoLockVar",
+ &mTestNamespaceGuid1,
+ (EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_NON_VOLATILE),
+ sizeof (Value),
+ &Value);
+ UT_ASSERT_TRUE ((Status == EFI_WRITE_PROTECTED) || (Status == EFI_INVALID_PARAMETER));
+
+ //
+ // Try to write a var that satisfies the can't have requirement
+ //
+ Status = gRT->SetVariable (L"CantHaveAttrNoLockVar",
+ &mTestNamespaceGuid1,
+ EFI_VARIABLE_BOOTSERVICE_ACCESS,
+ sizeof (Value),
+ &Value);
+ UT_ASSERT_NOT_EFI_ERROR (Status);
+
+ //
+ // Should be able to delete the var
+ //
+ Status = gRT->SetVariable (L"CantHaveAttrNoLockVar",
+ &mTestNamespaceGuid1,
+ 0,
+ 0,
+ NULL);
+ UT_ASSERT_NOT_EFI_ERROR (Status);
+
+ return UNIT_TEST_PASSED;
+} // TestCantHaveAttrNoLock
+
+UNIT_TEST_STATUS
+EFIAPI
+TestMaxSizeNamespaceNoLock (
+ IN UNIT_TEST_CONTEXT Context
+ )
+{
+ EFI_STATUS Status;
+ UINT8 Value1;
+ UINT32 Value2;
+ UINT8 *Buffer;
+
+ //
+ // Register a namespace-wide policy limiting max size to 4 bytes
+ //
+ Status = RegisterBasicVariablePolicy (mVarPol,
+ &mTestNamespaceGuid2,
+ NULL,
+ VARIABLE_POLICY_NO_MIN_SIZE,
+ 4,
+ VARIABLE_POLICY_NO_MUST_ATTR,
+ VARIABLE_POLICY_NO_CANT_ATTR,
+ VARIABLE_POLICY_TYPE_NO_LOCK);
+ UT_ASSERT_NOT_EFI_ERROR (Status);
+
+ //
+ // Try to write a var that is smaller than maxsize
+ //
+ Value1 = 0x34;
+ Status = gRT->SetVariable (L"MaxSizeNoLockVar",
+ &mTestNamespaceGuid2,
+ (EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS),
+ sizeof (Value1),
+ &Value1);
+ UT_ASSERT_NOT_EFI_ERROR (Status);
+
+ //
+ // Should be able to delete the var
+ //
+ Status = gRT->SetVariable (L"MaxSizeNoLockVar",
+ &mTestNamespaceGuid2,
+ 0,
+ 0,
+ NULL);
+ UT_ASSERT_NOT_EFI_ERROR (Status);
+
+ //
+ // Try to write a var of size that matches maxsize
+ //
+ Value2 = 0xa1b2c3d4;
+ Status = gRT->SetVariable (L"MaxSizeNoLockVar",
+ &mTestNamespaceGuid2,
+ (EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS),
+ sizeof (Value2),
+ &Value2);
+ UT_ASSERT_NOT_EFI_ERROR (Status);
+
+ //
+ // Should be able to delete the var
+ //
+ Status = gRT->SetVariable (L"MaxSizeNoLockVar",
+ &mTestNamespaceGuid2,
+ 0,
+ 0,
+ NULL);
+ UT_ASSERT_NOT_EFI_ERROR (Status);
+
+ //
+ // Try to write a var of size larger than maxsize
+ //
+ Buffer = AllocateZeroPool (40);
+ UT_ASSERT_NOT_NULL (Buffer);
+ Status = gRT->SetVariable (L"MaxSizeNoLockVar",
+ &mTestNamespaceGuid2,
+ (EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS),
+ 40,
+ Buffer);
+ UT_ASSERT_TRUE ((Status == EFI_WRITE_PROTECTED) || (Status == EFI_INVALID_PARAMETER));
+
+ FreePool (Buffer);
+
+ return UNIT_TEST_PASSED;
+} // TestMaxSizeNamespaceNoLock
+
+UNIT_TEST_STATUS
+EFIAPI
+TestMustHaveAttrWildcardNoLock (
+ IN UNIT_TEST_CONTEXT Context
+ )
+{
+ EFI_STATUS Status;
+ UINT8 Value;
+
+ Status = RegisterBasicVariablePolicy (mVarPol,
+ &mTestNamespaceGuid1,
+ L"MustHaveAttrWildcardNoLockVar####",
+ VARIABLE_POLICY_NO_MIN_SIZE,
+ VARIABLE_POLICY_NO_MAX_SIZE,
+ (EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS),
+ VARIABLE_POLICY_NO_CANT_ATTR,
+ VARIABLE_POLICY_TYPE_NO_LOCK);
+ UT_ASSERT_NOT_EFI_ERROR (Status);
+
+ //
+ // Try to write a var that doesn't have the must-have attributes
+ //
+ Value = 0x56;
+ Status = gRT->SetVariable (L"MustHaveAttrWildcardNoLockVar1573",
+ &mTestNamespaceGuid1,
+ EFI_VARIABLE_BOOTSERVICE_ACCESS,
+ sizeof (Value),
+ &Value);
+ UT_ASSERT_TRUE ((Status == EFI_WRITE_PROTECTED) || (Status == EFI_INVALID_PARAMETER));
+
+ //
+ // Try to write a var that has exactly the required attributes
+ //
+ Status = gRT->SetVariable (L"MustHaveAttrWildcardNoLockVar1234",
+ &mTestNamespaceGuid1,
+ (EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS),
+ sizeof (Value),
+ &Value);
+ UT_ASSERT_NOT_EFI_ERROR (Status);
+
+ //
+ // Should be able to delete the var
+ // NOTE: some implementations of VP will require the musthave attributes to be passed even when deleting
+ //
+ Status = gRT->SetVariable (L"MustHaveAttrWildcardNoLockVar1234",
+ &mTestNamespaceGuid1,
+ 0,
+ 0,
+ NULL);
+ UT_ASSERT_NOT_EFI_ERROR (Status);
+
+ //
+ // Try to write a var that has the required attributes and one extra attribute
+ //
+ Status = gRT->SetVariable (L"MustHaveAttrWildcardNoLockVar5612",
+ &mTestNamespaceGuid1,
+ (EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS),
+ sizeof (Value),
+ &Value);
+ UT_ASSERT_NOT_EFI_ERROR (Status);
+
+ //
+ // Should be able to delete the var
+ // NOTE: some implementations of VP will require the musthave attributes to be passed even when deleting
+ //
+ Status = gRT->SetVariable (L"MustHaveAttrWildcardNoLockVar5612",
+ &mTestNamespaceGuid1,
+ 0,
+ 0,
+ NULL);
+ UT_ASSERT_NOT_EFI_ERROR (Status);
+
+ return UNIT_TEST_PASSED;
+} // TestMustHaveAttrWildcardNoLock
+
+UNIT_TEST_STATUS
+EFIAPI
+TestPolicyprioritizationNoLock (
+ IN UNIT_TEST_CONTEXT Context
+ )
+{
+ EFI_STATUS Status;
+ UINT8 Value8;
+ UINT16 Value16;
+ UINT32 Value32;
+ UINT64 Value64;
+
+ //
+ // Register a policy targeting the specific var
+ //
+ Status = RegisterBasicVariablePolicy (mVarPol,
+ &mTestNamespaceGuid3,
+ L"PolicyPriorityTestVar123",
+ 8, // min size of UINT64
+ VARIABLE_POLICY_NO_MAX_SIZE,
+ VARIABLE_POLICY_NO_MUST_ATTR,
+ VARIABLE_POLICY_NO_CANT_ATTR,
+ VARIABLE_POLICY_TYPE_NO_LOCK);
+ UT_ASSERT_NOT_EFI_ERROR (Status);
+
+ //
+ // Register a policy with wildcards in the name
+ //
+ Status = RegisterBasicVariablePolicy (mVarPol,
+ &mTestNamespaceGuid3,
+ L"PolicyPriorityTestVar###",
+ 4, // min size of UINT32
+ VARIABLE_POLICY_NO_MAX_SIZE,
+ VARIABLE_POLICY_NO_MUST_ATTR,
+ VARIABLE_POLICY_NO_CANT_ATTR,
+ VARIABLE_POLICY_TYPE_NO_LOCK);
+ UT_ASSERT_NOT_EFI_ERROR (Status);
+
+ //
+ // Register a policy with wildcards in the name
+ //
+ Status = RegisterBasicVariablePolicy (mVarPol,
+ &mTestNamespaceGuid3,
+ NULL,
+ 2, // min size of UINT16
+ VARIABLE_POLICY_NO_MAX_SIZE,
+ VARIABLE_POLICY_NO_MUST_ATTR,
+ VARIABLE_POLICY_NO_CANT_ATTR,
+ VARIABLE_POLICY_TYPE_NO_LOCK);
+ UT_ASSERT_NOT_EFI_ERROR (Status);
+
+ //
+ // The idea is that the most specific policy is applied:
+ // For varname "TestVar", the namespace-wide one should apply: UINT16 minimum
+ // For varname "PolicyPriorityTestVar567" the wildcard policy should apply: UINT32 minimum
+ // For varname "PolicyPriorityTestVar123" the var-specific policy should apply: UINT64 minimum
+ //
+
+ //
+ // Let's confirm the namespace-wide policy enforcement
+ //
+ Value8 = 0x78;
+ Status = gRT->SetVariable (L"TestVar",
+ &mTestNamespaceGuid3,
+ (EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_NON_VOLATILE),
+ sizeof (Value8),
+ &Value8);
+ UT_ASSERT_TRUE ((Status == EFI_WRITE_PROTECTED) || (Status == EFI_INVALID_PARAMETER));
+
+ Value16 = 0x6543;
+ Status = gRT->SetVariable (L"TestVar",
+ &mTestNamespaceGuid3,
+ (EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_NON_VOLATILE),
+ sizeof (Value16),
+ &Value16);
+ UT_ASSERT_NOT_EFI_ERROR (Status);
+
+ //
+ // Let's confirm the wildcard policy enforcement
+ //
+ Value16 = 0xabba;
+ Status = gRT->SetVariable (L"PolicyPriorityTestVar567",
+ &mTestNamespaceGuid3,
+ (EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_NON_VOLATILE),
+ sizeof (Value16),
+ &Value16);
+ UT_ASSERT_TRUE ((Status == EFI_WRITE_PROTECTED) || (Status == EFI_INVALID_PARAMETER));
+
+ Value32 = 0xfedcba98;
+ Status = gRT->SetVariable (L"PolicyPriorityTestVar567",
+ &mTestNamespaceGuid3,
+ (EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_NON_VOLATILE),
+ sizeof (Value32),
+ &Value32);
+ UT_ASSERT_NOT_EFI_ERROR (Status);
+
+ //
+ // Let's confirm the var-specific policy enforcement
+ //
+ Value32 = 0x8d3f627c;
+ Status = gRT->SetVariable (L"PolicyPriorityTestVar123",
+ &mTestNamespaceGuid3,
+ (EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_NON_VOLATILE),
+ sizeof (Value32),
+ &Value32);
+ UT_ASSERT_TRUE ((Status == EFI_WRITE_PROTECTED) || (Status == EFI_INVALID_PARAMETER));
+
+ Value64 = 0xbebecdcdafaf6767;
+ Status = gRT->SetVariable (L"PolicyPriorityTestVar123",
+ &mTestNamespaceGuid3,
+ (EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_NON_VOLATILE),
+ sizeof (Value64),
+ &Value64);
+ UT_ASSERT_NOT_EFI_ERROR (Status);
+
+ return UNIT_TEST_PASSED;
+} // TestPolicyprioritizationNoLock
+
+/**
+ LockNow Policy tests.
+**/
+UNIT_TEST_STATUS
+EFIAPI
+TestExistingVarLockNow (
+ IN UNIT_TEST_CONTEXT Context
+ )
+{
+ EFI_STATUS Status;
+ UINT8 Value;
+
+ //
+ // Write a var that we'll protect next
+ //
+ Value = 0x78;
+ Status = gRT->SetVariable (L"ExistingLockNowVar",
+ &mTestNamespaceGuid1,
+ (EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_NON_VOLATILE),
+ sizeof (Value),
+ &Value);
+ UT_ASSERT_NOT_EFI_ERROR (Status);
+
+ //
+ // Register a LockNow policy targeting the var
+ //
+ Status = RegisterBasicVariablePolicy (mVarPol,
+ &mTestNamespaceGuid1,
+ L"ExistingLockNowVar",
+ 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);
+ UT_ASSERT_NOT_EFI_ERROR (Status);
+
+ //
+ // Attempt to modify the locked var
+ //
+ Value = 0xA5;
+ Status = gRT->SetVariable (L"ExistingLockNowVar",
+ &mTestNamespaceGuid1,
+ (EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_NON_VOLATILE),
+ sizeof (Value),
+ &Value);
+ UT_ASSERT_STATUS_EQUAL (Status, EFI_WRITE_PROTECTED);
+
+ //
+ // Attempt to delete the locked var
+ //
+ Status = gRT->SetVariable (L"ExistingLockNowVar",
+ &mTestNamespaceGuid1,
+ 0,
+ 0,
+ NULL);
+ UT_ASSERT_STATUS_EQUAL (Status, EFI_WRITE_PROTECTED);
+
+ //
+ // This variable is deleted in final cleanup.
+ //
+
+ return UNIT_TEST_PASSED;
+} // TestExistingVarLockNow
+
+UNIT_TEST_STATUS
+EFIAPI
+TestNonexistentVarLockNow (
+ IN UNIT_TEST_CONTEXT Context
+ )
+{
+ EFI_STATUS Status;
+ UINT8 Value;
+ UINTN Size;
+
+ //
+ // Make sure the variable we're about to create the policy for doesn't exist
+ //
+ Size = 0;
+ Status = gRT->GetVariable (L"NonexistentLockNowVar",
+ &mTestNamespaceGuid1,
+ NULL,
+ &Size,
+ NULL);
+ UT_ASSERT_STATUS_EQUAL (Status, EFI_NOT_FOUND);
+
+ //
+ // Register a LockNow policy targeting the var
+ //
+ Status = RegisterBasicVariablePolicy (mVarPol,
+ &mTestNamespaceGuid1,
+ L"NonexistentLockNowVar",
+ 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);
+ UT_ASSERT_NOT_EFI_ERROR (Status);
+
+ //
+ // Attempt to create the locked var
+ //
+ Value = 0xA5;
+ Status = gRT->SetVariable (L"NonexistentLockNowVar",
+ &mTestNamespaceGuid1,
+ (EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_NON_VOLATILE),
+ sizeof (Value),
+ &Value);
+ UT_ASSERT_STATUS_EQUAL (Status, EFI_WRITE_PROTECTED);
+
+ return UNIT_TEST_PASSED;
+} // TestNonexistentVarLockNow
+
+/**
+ LockOnCreate Policy tests.
+**/
+UNIT_TEST_STATUS
+EFIAPI
+TestExistingVarLockOnCreate (
+ IN UNIT_TEST_CONTEXT Context
+ )
+{
+ EFI_STATUS Status;
+ UINT8 Value;
+
+ //
+ // Write a var that we'll protect later
+ //
+ Value = 0x78;
+ Status = gRT->SetVariable (L"ExistingLockOnCreateVar",
+ &mTestNamespaceGuid1,
+ (EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_NON_VOLATILE),
+ sizeof (Value),
+ &Value);
+ UT_ASSERT_NOT_EFI_ERROR (Status);
+
+ //
+ // Register a LockNow policy targeting the var
+ //
+ Status = RegisterBasicVariablePolicy (mVarPol,
+ &mTestNamespaceGuid1,
+ L"ExistingLockOnCreateVar",
+ VARIABLE_POLICY_NO_MIN_SIZE,
+ VARIABLE_POLICY_NO_MAX_SIZE,
+ VARIABLE_POLICY_NO_MUST_ATTR,
+ VARIABLE_POLICY_NO_CANT_ATTR,
+ VARIABLE_POLICY_TYPE_LOCK_ON_CREATE);
+ UT_ASSERT_NOT_EFI_ERROR (Status);
+
+ //
+ // Attempt to modify the locked var
+ //
+ Value = 0xA5;
+ Status = gRT->SetVariable (L"ExistingLockOnCreateVar",
+ &mTestNamespaceGuid1,
+ (EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_NON_VOLATILE),
+ sizeof (Value),
+ &Value);
+ UT_ASSERT_STATUS_EQUAL (Status, EFI_WRITE_PROTECTED);
+
+ //
+ // Attempt to delete the locked var
+ //
+ Status = gRT->SetVariable (L"ExistingLockOnCreateVar",
+ &mTestNamespaceGuid1,
+ 0,
+ 0,
+ NULL);
+ UT_ASSERT_STATUS_EQUAL (Status, EFI_WRITE_PROTECTED);
+
+ //
+ // This variable is deleted in final cleanup.
+ //
+
+ return UNIT_TEST_PASSED;
+} // TestExistingVarLockOnCreate
+
+UNIT_TEST_STATUS
+EFIAPI
+TestNonexistentVarLockOnCreate (
+ IN UNIT_TEST_CONTEXT Context
+ )
+{
+ EFI_STATUS Status;
+ UINT8 Value1;
+ UINT32 Value2;
+ UINTN Size;
+
+ //
+ // Make sure the variable we're about to create the policy for doesn't exist
+ //
+ Size = 0;
+ Status = gRT->GetVariable (L"NonexistentLockOnCreateVar",
+ &mTestNamespaceGuid1,
+ NULL,
+ &Size,
+ NULL);
+ UT_ASSERT_STATUS_EQUAL (Status, EFI_NOT_FOUND);
+
+ //
+ // Register a LockOnCreate policy targeting the var
+ //
+ Status = RegisterBasicVariablePolicy (mVarPol,
+ &mTestNamespaceGuid1,
+ L"NonexistentLockOnCreateVar",
+ 2, // min size of 2 bytes, UINT16+
+ VARIABLE_POLICY_NO_MAX_SIZE,
+ EFI_VARIABLE_RUNTIME_ACCESS, // must have RT attr
+ VARIABLE_POLICY_NO_CANT_ATTR,
+ VARIABLE_POLICY_TYPE_LOCK_ON_CREATE);
+ UT_ASSERT_NOT_EFI_ERROR (Status);
+
+ //
+ // Attempt to create the var, but smaller than min size
+ //
+ Value1 = 0xA5;
+ Status = gRT->SetVariable (L"NonexistentLockOnCreateVar",
+ &mTestNamespaceGuid1,
+ (EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE),
+ sizeof (Value1),
+ &Value1);
+ UT_ASSERT_TRUE ((Status == EFI_WRITE_PROTECTED) || (Status == EFI_INVALID_PARAMETER));
+
+ //
+ // Now let's make sure attribute req is enforced
+ //
+ Value2 = 0x43218765;
+ Status = gRT->SetVariable (L"NonexistentLockOnCreateVar",
+ &mTestNamespaceGuid1,
+ (EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_NON_VOLATILE),
+ sizeof (Value2),
+ &Value2);
+ UT_ASSERT_TRUE ((Status == EFI_WRITE_PROTECTED) || (Status == EFI_INVALID_PARAMETER));
+
+ //
+ // Now let's create a valid variable
+ //
+ Value2 = 0x43218765;
+ Status = gRT->SetVariable (L"NonexistentLockOnCreateVar",
+ &mTestNamespaceGuid1,
+ (EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE),
+ sizeof (Value2),
+ &Value2);
+ UT_ASSERT_NOT_EFI_ERROR (Status);
+
+ //
+ // Let's make sure we can't modify it
+ //
+ Value2 = 0xa5a5b6b6;
+ Status = gRT->SetVariable (L"NonexistentLockOnCreateVar",
+ &mTestNamespaceGuid1,
+ (EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE),
+ sizeof (Value2),
+ &Value2);
+ UT_ASSERT_STATUS_EQUAL (Status, EFI_WRITE_PROTECTED);
+
+ //
+ // Finally, let's make sure we can't delete it
+ //
+ Status = gRT->SetVariable (L"NonexistentLockOnCreateVar",
+ &mTestNamespaceGuid1,
+ 0,
+ 0,
+ NULL);
+ UT_ASSERT_STATUS_EQUAL (Status, EFI_WRITE_PROTECTED);
+
+ //
+ // This variable is deleted in final cleanup.
+ //
+
+ return UNIT_TEST_PASSED;
+} // TestNonexistentVarLockOnCreate
+
+/**
+ LockOnVarState Policy tests.
+**/
+UNIT_TEST_STATUS
+EFIAPI
+TestLockOnVarStateBeforeCreate (
+ IN UNIT_TEST_CONTEXT Context
+ )
+{
+ EFI_STATUS Status;
+ UINTN Size;
+ UINT8 Value;
+
+ //
+ // First of all, let's make sure the var we're trying to protect doesn't exist
+ //
+ Size = 0;
+ Status = gRT->GetVariable (L"NonexistentLockOnVarStateVar",
+ &mTestNamespaceGuid1,
+ NULL,
+ &Size,
+ NULL);
+ UT_ASSERT_STATUS_EQUAL (Status, EFI_NOT_FOUND);
+
+ //
+ // Good, now let's create a policy
+ //
+ Status = RegisterVarStateVariablePolicy (mVarPol,
+ &mTestNamespaceGuid1,
+ L"NonexistentLockOnVarStateVar",
+ VARIABLE_POLICY_NO_MIN_SIZE,
+ VARIABLE_POLICY_NO_MAX_SIZE,
+ VARIABLE_POLICY_NO_MUST_ATTR,
+ VARIABLE_POLICY_NO_CANT_ATTR,
+ &mTestNamespaceGuid1,
+ L"Trigger1",
+ 0x7E);
+ UT_ASSERT_NOT_EFI_ERROR (Status);
+
+ //
+ // Now we write the trigger var
+ //
+ Value = 0x7E;
+ Status = gRT->SetVariable (L"Trigger1",
+ &mTestNamespaceGuid1,
+ (EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_NON_VOLATILE),
+ sizeof (Value),
+ &Value);
+ UT_ASSERT_NOT_EFI_ERROR (Status);
+
+ //
+ // Ok, now we attempt to write a var protected by the trigger
+ //
+ Value = 0xFA;
+ Status = gRT->SetVariable (L"NonexistentLockOnVarStateVar",
+ &mTestNamespaceGuid1,
+ (EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_NON_VOLATILE),
+ sizeof (Value),
+ &Value);
+ UT_ASSERT_STATUS_EQUAL (Status, EFI_WRITE_PROTECTED);
+
+ //
+ // Let's modify the trigger var and "untrigger" the policy
+ //
+ Value = 0x38;
+ Status = gRT->SetVariable (L"Trigger1",
+ &mTestNamespaceGuid1,
+ (EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_NON_VOLATILE),
+ sizeof (Value),
+ &Value);
+ UT_ASSERT_NOT_EFI_ERROR (Status);
+
+ //
+ // Now we should be able to create the var targeted by the policy
+ //
+ Value = 0x23;
+ Status = gRT->SetVariable (L"NonexistentLockOnVarStateVar",
+ &mTestNamespaceGuid1,
+ (EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_NON_VOLATILE),
+ sizeof (Value),
+ &Value);
+ UT_ASSERT_NOT_EFI_ERROR (Status);
+
+ //
+ // Cleanup: delete the trigger and the protected var
+ //
+ Status = gRT->SetVariable (L"Trigger1",
+ &mTestNamespaceGuid1,
+ 0,
+ 0,
+ NULL);
+ UT_ASSERT_NOT_EFI_ERROR (Status);
+
+ Status = gRT->SetVariable (L"NonexistentLockOnVarStateVar",
+ &mTestNamespaceGuid1,
+ 0,
+ 0,
+ NULL);
+ UT_ASSERT_NOT_EFI_ERROR (Status);
+
+ return UNIT_TEST_PASSED;
+} // TestLockOnVarStateBeforeCreate
+
+UNIT_TEST_STATUS
+EFIAPI
+TestLockOnVarStateAfterCreate (
+ IN UNIT_TEST_CONTEXT Context
+ )
+{
+ EFI_STATUS Status;
+ UINT8 Value;
+
+ //
+ // Let's create a policy
+ //
+ Status = RegisterVarStateVariablePolicy (mVarPol,
+ &mTestNamespaceGuid1,
+ L"ExistingLockOnVarStateVar",
+ VARIABLE_POLICY_NO_MIN_SIZE,
+ VARIABLE_POLICY_NO_MAX_SIZE,
+ VARIABLE_POLICY_NO_MUST_ATTR,
+ VARIABLE_POLICY_NO_CANT_ATTR,
+ &mTestNamespaceGuid1,
+ L"Trigger2",
+ 0x5C);
+ UT_ASSERT_NOT_EFI_ERROR (Status);
+
+ //
+ // Should be able to write targeted var since the policy isn't active yet.
+ //
+ Value = 0x17;
+ Status = gRT->SetVariable (L"ExistingLockOnVarStateVar",
+ &mTestNamespaceGuid1,
+ (EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_NON_VOLATILE),
+ sizeof (Value),
+ &Value);
+ UT_ASSERT_NOT_EFI_ERROR (Status);
+
+ //
+ // Let's modify the var to make sure the policy isn't acting like a lock-on-create one
+ //
+ Value = 0x30;
+ Status = gRT->SetVariable (L"ExistingLockOnVarStateVar",
+ &mTestNamespaceGuid1,
+ (EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_NON_VOLATILE),
+ sizeof (Value),
+ &Value);
+ UT_ASSERT_NOT_EFI_ERROR (Status);
+
+ //
+ // Now we trigger the policy
+ //
+ Value = 0x5C;
+ Status = gRT->SetVariable (L"Trigger2",
+ &mTestNamespaceGuid1,
+ (EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_NON_VOLATILE),
+ sizeof (Value),
+ &Value);
+ UT_ASSERT_NOT_EFI_ERROR (Status);
+
+ //
+ // Let's now verify the variable is protected
+ //
+ Value = 0xB9;
+ Status = gRT->SetVariable (L"ExistingLockOnVarStateVar",
+ &mTestNamespaceGuid1,
+ (EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_NON_VOLATILE),
+ sizeof (Value),
+ &Value);
+ UT_ASSERT_STATUS_EQUAL (Status, EFI_WRITE_PROTECTED);
+
+ //
+ // Ok, to clean up, we need to remove the trigger var, so delete it, and then delete the target var
+ //
+ Status = gRT->SetVariable (L"Trigger2",
+ &mTestNamespaceGuid1,
+ 0,
+ 0,
+ NULL);
+ UT_ASSERT_NOT_EFI_ERROR (Status);
+
+ Status = gRT->SetVariable (L"ExistingLockOnVarStateVar",
+ &mTestNamespaceGuid1,
+ 0,
+ 0,
+ NULL);
+ UT_ASSERT_NOT_EFI_ERROR (Status);
+
+ return UNIT_TEST_PASSED;
+} // TestLockOnVarStateAfterCreate
+
+UNIT_TEST_STATUS
+EFIAPI
+TestLockOnVarStateInvalidLargeTrigger (
+ IN UNIT_TEST_CONTEXT Context
+ )
+{
+ EFI_STATUS Status;
+ UINT16 Value;
+
+ //
+ // First let's create a variable policy
+ //
+ Status = RegisterVarStateVariablePolicy (mVarPol,
+ &mTestNamespaceGuid1,
+ L"InvalidLargeTriggerLockOnVarStateVar",
+ VARIABLE_POLICY_NO_MIN_SIZE,
+ VARIABLE_POLICY_NO_MAX_SIZE,
+ VARIABLE_POLICY_NO_MUST_ATTR,
+ VARIABLE_POLICY_NO_CANT_ATTR,
+ &mTestNamespaceGuid1,
+ L"Trigger3",
+ 0x5C);
+ UT_ASSERT_NOT_EFI_ERROR (Status);
+
+ //
+ // Now attempt to trigger the lock but with a variable larger than one byte
+ //
+ Value = 0x8085;
+ Status = gRT->SetVariable (L"Trigger3",
+ &mTestNamespaceGuid1,
+ (EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_NON_VOLATILE),
+ sizeof (Value),
+ &Value);
+ UT_ASSERT_NOT_EFI_ERROR (Status);
+
+ //
+ // Should still be able to create the targeted var
+ //
+ Value = 0x1234;
+ Status = gRT->SetVariable (L"InvalidLargeTriggerLockOnVarStateVar",
+ &mTestNamespaceGuid1,
+ (EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_NON_VOLATILE),
+ sizeof (Value),
+ &Value);
+ UT_ASSERT_NOT_EFI_ERROR (Status);
+
+ //
+ // Let's clean up by deleting the invalid trigger and the targeted var
+ //
+ Status = gRT->SetVariable (L"Trigger3",
+ &mTestNamespaceGuid1,
+ 0,
+ 0,
+ NULL);
+ UT_ASSERT_NOT_EFI_ERROR (Status);
+
+ Status = gRT->SetVariable (L"InvalidLargeTriggerLockOnVarStateVar",
+ &mTestNamespaceGuid1,
+ 0,
+ 0,
+ NULL);
+ UT_ASSERT_NOT_EFI_ERROR (Status);
+
+ return UNIT_TEST_PASSED;
+} // TestLockOnVarStateInvalidLargeTrigger
+
+UNIT_TEST_STATUS
+EFIAPI
+TestLockOnVarStateWrongValueTrigger (
+ IN UNIT_TEST_CONTEXT Context
+ )
+{
+ EFI_STATUS Status;
+ UINT8 Value;
+
+ //
+ // First let's create a variable policy
+ //
+ Status = RegisterVarStateVariablePolicy (mVarPol,
+ &mTestNamespaceGuid1,
+ L"WrongValueTriggerLockOnVarStateVar",
+ VARIABLE_POLICY_NO_MIN_SIZE,
+ VARIABLE_POLICY_NO_MAX_SIZE,
+ VARIABLE_POLICY_NO_MUST_ATTR,
+ VARIABLE_POLICY_NO_CANT_ATTR,
+ &mTestNamespaceGuid1,
+ L"Trigger4",
+ 0xCA);
+ UT_ASSERT_NOT_EFI_ERROR (Status);
+
+ //
+ // Now attempt to trigger the lock but with a wrong value
+ //
+ Value = 0x80;
+ Status = gRT->SetVariable (L"Trigger4",
+ &mTestNamespaceGuid1,
+ (EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_NON_VOLATILE),
+ sizeof (Value),
+ &Value);
+ UT_ASSERT_NOT_EFI_ERROR (Status);
+
+ //
+ // Should still be able to create the targeted var
+ //
+ Value = 0x14;
+ Status = gRT->SetVariable (L"WrongValueTriggerLockOnVarStateVar",
+ &mTestNamespaceGuid1,
+ (EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_NON_VOLATILE),
+ sizeof (Value),
+ &Value);
+ UT_ASSERT_NOT_EFI_ERROR (Status);
+
+ //
+ // Let's clean up by deleting the invalid trigger and the targeted var
+ //
+ Status = gRT->SetVariable (L"Trigger4",
+ &mTestNamespaceGuid1,
+ 0,
+ 0,
+ NULL);
+ UT_ASSERT_NOT_EFI_ERROR (Status);
+
+ Status = gRT->SetVariable (L"WrongValueTriggerLockOnVarStateVar",
+ &mTestNamespaceGuid1,
+ 0,
+ 0,
+ NULL);
+ UT_ASSERT_NOT_EFI_ERROR (Status);
+
+ return UNIT_TEST_PASSED;
+} // TestLockOnVarStateWrongValueTrigger
+
+/**
+ Invalid policy tests.
+**/
+UNIT_TEST_STATUS
+EFIAPI
+TestInvalidAttributesPolicy (
+ IN UNIT_TEST_CONTEXT Context
+ )
+{
+ EFI_STATUS Status;
+
+ //
+ // The only must/can't have attributes supported by VPE are NV, BS, and RT. They are 1, 2, and 4, respectively.
+ // Let's try some bits higher than that?
+ //
+
+ //
+ // Trying must have attribute 0x8 which is EFI_VARIABLE_HARDWARE_ERROR_RECORD
+ //
+ Status = RegisterBasicVariablePolicy (mVarPol,
+ &mTestNamespaceGuid1,
+ L"InvalidMustHaveAttributesPolicyVar1",
+ VARIABLE_POLICY_NO_MIN_SIZE,
+ VARIABLE_POLICY_NO_MAX_SIZE,
+ EFI_VARIABLE_HARDWARE_ERROR_RECORD,
+ VARIABLE_POLICY_NO_CANT_ATTR,
+ VARIABLE_POLICY_TYPE_NO_LOCK);
+ UT_LOG_INFO ("Setting must have attr to EFI_VARIABLE_HARDWARE_ERROR_RECORD returned %r\n", Status);
+
+ //
+ // Let's try 0x10 - EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS, a deprecated attribute
+ //
+ Status = RegisterBasicVariablePolicy (mVarPol,
+ &mTestNamespaceGuid1,
+ L"InvalidMustHaveAttributesPolicyVar2",
+ VARIABLE_POLICY_NO_MIN_SIZE,
+ VARIABLE_POLICY_NO_MAX_SIZE,
+ EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS,
+ VARIABLE_POLICY_NO_CANT_ATTR,
+ VARIABLE_POLICY_TYPE_NO_LOCK);
+ UT_LOG_INFO ("Setting must have attr to EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS returned %r\n", Status);
+
+ //
+ // Let's try 0x20 - EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS
+ //
+ Status = RegisterBasicVariablePolicy (mVarPol,
+ &mTestNamespaceGuid1,
+ L"InvalidMustHaveAttributesPolicyVar3",
+ VARIABLE_POLICY_NO_MIN_SIZE,
+ VARIABLE_POLICY_NO_MAX_SIZE,
+ EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS,
+ VARIABLE_POLICY_NO_CANT_ATTR,
+ VARIABLE_POLICY_TYPE_NO_LOCK);
+ UT_LOG_INFO ("Setting must have attr to EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS returned %r\n", Status);
+
+ //
+ // Let's try something wild, like 0x4000
+ //
+ Status = RegisterBasicVariablePolicy (mVarPol,
+ &mTestNamespaceGuid1,
+ L"InvalidMustHaveAttributesPolicyVar4",
+ VARIABLE_POLICY_NO_MIN_SIZE,
+ VARIABLE_POLICY_NO_MAX_SIZE,
+ 0x4000,
+ VARIABLE_POLICY_NO_CANT_ATTR,
+ VARIABLE_POLICY_TYPE_NO_LOCK);
+ UT_LOG_INFO ("Setting must have attr to 0x4000 returned %r\n", Status);
+
+ //
+ // Now repeat the same tests, but for the can't-have param
+ //
+ Status = RegisterBasicVariablePolicy (mVarPol,
+ &mTestNamespaceGuid1,
+ L"InvalidCantHaveAttributesPolicyVar1",
+ VARIABLE_POLICY_NO_MIN_SIZE,
+ VARIABLE_POLICY_NO_MAX_SIZE,
+ VARIABLE_POLICY_NO_MUST_ATTR,
+ EFI_VARIABLE_HARDWARE_ERROR_RECORD,
+ VARIABLE_POLICY_TYPE_NO_LOCK);
+ UT_LOG_INFO ("Setting cant have attr to EFI_VARIABLE_HARDWARE_ERROR_RECORD returned %r\n", Status);
+
+ Status = RegisterBasicVariablePolicy (mVarPol,
+ &mTestNamespaceGuid1,
+ L"InvalidCantHaveAttributesPolicyVar2",
+ VARIABLE_POLICY_NO_MIN_SIZE,
+ VARIABLE_POLICY_NO_MAX_SIZE,
+ VARIABLE_POLICY_NO_MUST_ATTR,
+ EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS,
+ VARIABLE_POLICY_TYPE_NO_LOCK);
+ UT_LOG_INFO ("Setting cant have attr to EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS returned %r\n", Status);
+
+ Status = RegisterBasicVariablePolicy (mVarPol,
+ &mTestNamespaceGuid1,
+ L"InvalidCantHaveAttributesPolicyVar3",
+ VARIABLE_POLICY_NO_MIN_SIZE,
+ VARIABLE_POLICY_NO_MAX_SIZE,
+ VARIABLE_POLICY_NO_MUST_ATTR,
+ EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS,
+ VARIABLE_POLICY_TYPE_NO_LOCK);
+ UT_LOG_INFO ("Setting cant have attr to EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS returned %r\n", Status);
+
+ Status = RegisterBasicVariablePolicy (mVarPol,
+ &mTestNamespaceGuid1,
+ L"InvalidCantHaveAttributesPolicyVar4",
+ VARIABLE_POLICY_NO_MIN_SIZE,
+ VARIABLE_POLICY_NO_MAX_SIZE,
+ VARIABLE_POLICY_NO_MUST_ATTR,
+ 0x4000,
+ VARIABLE_POLICY_TYPE_NO_LOCK);
+ UT_LOG_INFO ("Setting cant have attr to 0x4000 returned %r\n", Status);
+
+ return UNIT_TEST_PASSED;
+} // TestInvalidAttributesPolicy
+
+UNIT_TEST_STATUS
+EFIAPI
+TestLargeMinSizePolicy (
+ IN UNIT_TEST_CONTEXT Context
+ )
+{
+ EFI_STATUS Status;
+
+ //
+ // Let's set the min size to 2GB and see what happens
+ //
+ Status = RegisterBasicVariablePolicy (mVarPol,
+ &mTestNamespaceGuid1,
+ L"LargeMinSizeInvalidPolicyVar",
+ 0x80000000,
+ VARIABLE_POLICY_NO_MAX_SIZE,
+ VARIABLE_POLICY_NO_MUST_ATTR,
+ VARIABLE_POLICY_NO_CANT_ATTR,
+ VARIABLE_POLICY_TYPE_NO_LOCK);
+
+ UT_LOG_INFO ("Setting min size to 0x80000000 returned %r\n", Status);
+
+ return UNIT_TEST_PASSED;
+} // TestLargeMinSizePolicy
+
+UNIT_TEST_STATUS
+EFIAPI
+TestZeroMaxSizePolicy (
+ IN UNIT_TEST_CONTEXT Context
+ )
+{
+ EFI_STATUS Status;
+
+ //
+ // Let's set the max size to 0 and see what happens
+ //
+ Status = RegisterBasicVariablePolicy (mVarPol,
+ &mTestNamespaceGuid1,
+ L"ZeroMinSizeInvalidPolicyVar",
+ VARIABLE_POLICY_NO_MIN_SIZE,
+ 0,
+ VARIABLE_POLICY_NO_MUST_ATTR,
+ VARIABLE_POLICY_NO_CANT_ATTR,
+ VARIABLE_POLICY_TYPE_NO_LOCK);
+ //UT_ASSERT_NOT_EQUAL (Status, EFI_SUCCESS); // this fails on QC. Real bug? Do we care?
+ UT_LOG_INFO ("Setting max size to 0 returned %r\n", Status);
+
+ return UNIT_TEST_PASSED;
+} // TestZeroMaxSizePolicy
+
+UNIT_TEST_STATUS
+EFIAPI
+TestInvalidPolicyTypePolicy (
+ IN UNIT_TEST_CONTEXT Context
+ )
+{
+ EFI_STATUS Status;
+
+ //
+ // Let's set policy type to an invalid value and see what happens
+ // Valid ones are:
+ // VARIABLE_POLICY_TYPE_NO_LOCK 0
+ // VARIABLE_POLICY_TYPE_LOCK_NOW 1
+ // VARIABLE_POLICY_TYPE_LOCK_ON_CREATE 2
+ // VARIABLE_POLICY_TYPE_LOCK_ON_VAR_STATE 3
+ //
+ Status = RegisterBasicVariablePolicy (mVarPol,
+ &mTestNamespaceGuid1,
+ L"InvalidPolicyTypePolicyVar",
+ VARIABLE_POLICY_NO_MIN_SIZE,
+ VARIABLE_POLICY_NO_MAX_SIZE,
+ VARIABLE_POLICY_NO_MUST_ATTR,
+ VARIABLE_POLICY_NO_CANT_ATTR,
+ 4);
+ UT_ASSERT_NOT_EQUAL (Status, EFI_SUCCESS);
+
+ Status = RegisterBasicVariablePolicy (mVarPol,
+ &mTestNamespaceGuid1,
+ L"InvalidPolicyTypePolicyVar",
+ VARIABLE_POLICY_NO_MIN_SIZE,
+ VARIABLE_POLICY_NO_MAX_SIZE,
+ VARIABLE_POLICY_NO_MUST_ATTR,
+ VARIABLE_POLICY_NO_CANT_ATTR,
+ 147);
+ UT_ASSERT_NOT_EQUAL (Status, EFI_SUCCESS);
+
+ return UNIT_TEST_PASSED;
+} // TestInvalidPolicyTypePolicy
+
+/**
+ Test dumping policy.
+**/
+UNIT_TEST_STATUS
+EFIAPI
+TestDumpPolicy (
+ IN UNIT_TEST_CONTEXT Context
+ )
+{
+ EFI_STATUS Status;
+ UINT8* Buffer;
+ UINT32 Size;
+
+ //
+ // First let's call DumpVariablePolicy with null buffer to get size
+ //
+ Size = 0;
+ Status = mVarPol->DumpVariablePolicy (NULL, &Size);
+ UT_ASSERT_STATUS_EQUAL (Status, EFI_BUFFER_TOO_SMALL);
+
+ //
+ // Now we allocate the buffer for the dump
+ //
+ Buffer = NULL;
+ Buffer = AllocatePool (Size);
+ UT_ASSERT_NOT_NULL (Buffer);
+
+ //
+ // Now we get the dump. In this test we will not analyze the dump.
+ //
+ Status = mVarPol->DumpVariablePolicy (Buffer, &Size);
+ UT_ASSERT_NOT_EFI_ERROR (Status);
+
+ return UNIT_TEST_PASSED;
+} // TestDumpPolicy
+
+/**
+ Test policy version.
+**/
+UNIT_TEST_STATUS
+EFIAPI
+TestPolicyVersion (
+ IN UNIT_TEST_CONTEXT Context
+ )
+{
+ EFI_STATUS Status;
+ VARIABLE_POLICY_ENTRY *NewEntry;
+
+ //
+ // Create the new entry using a helper lib
+ //
+ NewEntry = NULL;
+ Status = CreateBasicVariablePolicy (&mTestNamespaceGuid1,
+ L"PolicyVersionTestNoLockVar",
+ VARIABLE_POLICY_NO_MIN_SIZE,
+ 4, // max size of 4 bytes
+ VARIABLE_POLICY_NO_MUST_ATTR,
+ VARIABLE_POLICY_NO_CANT_ATTR,
+ VARIABLE_POLICY_TYPE_NO_LOCK,
+ &NewEntry);
+ UT_ASSERT_NOT_EFI_ERROR (Status);
+
+ NewEntry->Version = 0x1234;
+ Status = mVarPol->RegisterVariablePolicy (NewEntry);
+ UT_LOG_INFO ("Registering policy entry with an unknown version status: %r\n", Status);
+
+ FreePool (NewEntry);
+
+ return UNIT_TEST_PASSED;
+} // TestPolicyVersion
+
+/**
+ Lock Policy Tests.
+**/
+UNIT_TEST_STATUS
+EFIAPI
+LockPolicyEngineTests (
+ IN UNIT_TEST_CONTEXT Context
+ )
+{
+ EFI_STATUS Status;
+ UINT16 Value;
+ UINT64 Value64;
+ BOOLEAN State;
+
+ //
+ // First let's register a policy that we'll test after VPE lock
+ //
+ Status = RegisterBasicVariablePolicy (mVarPol,
+ &mTestNamespaceGuid1,
+ L"BeforeVpeLockNoLockPolicyVar",
+ VARIABLE_POLICY_NO_MIN_SIZE,
+ 4, // max size of 4 bytes
+ VARIABLE_POLICY_NO_MUST_ATTR,
+ VARIABLE_POLICY_NO_CANT_ATTR,
+ VARIABLE_POLICY_TYPE_LOCK_ON_CREATE);
+ UT_ASSERT_NOT_EFI_ERROR (Status);
+
+ //
+ // Now, lock VPE!
+ //
+ Status = mVarPol->LockVariablePolicy ();
+ UT_ASSERT_NOT_EFI_ERROR (Status);
+
+ //
+ // See if we can lock it again?
+ //
+ Status = mVarPol->LockVariablePolicy ();
+ UT_LOG_INFO ("Locking VPE for second time returned %r\n", Status);
+
+ //
+ // Let's confirm one of the policies from prior test suites is still enforced
+ // Attempt to delete a locked var
+ //
+ Status = gRT->SetVariable (L"ExistingLockNowVar",
+ &mTestNamespaceGuid1,
+ 0,
+ 0,
+ NULL);
+ UT_ASSERT_STATUS_EQUAL (Status, EFI_WRITE_PROTECTED);
+
+ //
+ // We'll make sure the policy from earlier in this test case is actively filtering out by size
+ //
+ Value64 = 0x3829fed212345678;
+ Status = gRT->SetVariable (L"BeforeVpeLockNoLockPolicyVar",
+ &mTestNamespaceGuid1,
+ (EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS),
+ sizeof (Value64),
+ &Value64);
+ UT_ASSERT_TRUE ((Status == EFI_WRITE_PROTECTED) || (Status == EFI_INVALID_PARAMETER));
+
+ //
+ // Let's create the variable from the policy now
+ //
+ Value = 0x323f;
+ Status = gRT->SetVariable (L"BeforeVpeLockNoLockPolicyVar",
+ &mTestNamespaceGuid1,
+ (EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS),
+ sizeof (Value),
+ &Value);
+ UT_ASSERT_NOT_EFI_ERROR (Status);
+
+ //
+ // Now confirm that the var is locked after creation
+ //
+ Value = 0x1212;
+ Status = gRT->SetVariable (L"BeforeVpeLockNoLockPolicyVar",
+ &mTestNamespaceGuid1,
+ (EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS),
+ sizeof (Value),
+ &Value);
+ UT_ASSERT_STATUS_EQUAL (Status, EFI_WRITE_PROTECTED);
+
+ //
+ // Let's attempt to register a new policy, it should fail
+ //
+ Status = RegisterBasicVariablePolicy (mVarPol,
+ &mTestNamespaceGuid1,
+ L"AfterVpeLockNowPolicyVar",
+ 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);
+ UT_ASSERT_NOT_EQUAL (Status, EFI_SUCCESS);
+
+ //
+ // Make sure VPE is enabled
+ //
+ Status = mVarPol->IsVariablePolicyEnabled (&State);
+ UT_ASSERT_NOT_EFI_ERROR (Status);
+ UT_ASSERT_EQUAL (State, TRUE);
+
+ //
+ // Finally, make sure we can't disable VPE
+ //
+ Status = mVarPol->DisableVariablePolicy ();
+ UT_ASSERT_NOT_EQUAL (Status, EFI_SUCCESS);
+
+ return UNIT_TEST_PASSED;
+} // LockPolicyEngineTests
+
+/**
+ Save context and reboot after the lock policy test suite.
+**/
+STATIC
+VOID
+EFIAPI
+SaveStateAndReboot (
+ VOID
+ )
+{
+ EFI_STATUS Status;
+
+ //
+ // Now, save all the data associated with this framework.
+ // TODO: Need to add to the UnitTestFrameworkPkg
+ Status = SaveFrameworkState( NULL, 0 );
+
+ //
+ // If we're all good, let's book...
+ if (!EFI_ERROR( Status ))
+ {
+ //
+ // Next, we want to update the BootNext variable to USB
+ // so that we have a fighting chance of coming back here.
+ //
+ // TODO: Need to add to the UnitTestFrameworkPkg
+ // SetBootNextDevice();
+
+ //
+ // Reset
+ DEBUG(( DEBUG_INFO, "%a - Rebooting! Launch this test again once booted.\n", __FUNCTION__ ));
+ gRT->ResetSystem( EfiResetCold, EFI_SUCCESS, 0, NULL );
+ DEBUG(( DEBUG_ERROR, "%a - Unit test failed to quit! Framework can no longer be used!\n", __FUNCTION__ ));
+
+ //
+ // We REALLY shouldn't be here.
+ Status = EFI_ABORTED;
+ }
+
+ return;
+} // SaveContextAndReboot
+
+STATIC
+VOID
+IgnoreContextAndReboot (
+ IN UNIT_TEST_CONTEXT Context
+ )
+{
+ // Just a wrapper for prototype reuse.
+ SaveStateAndReboot();
+}
+
+/**
+ Disable policy tests.
+**/
+UNIT_TEST_STATUS
+EFIAPI
+DisablePolicyEngineTests (
+ IN UNIT_TEST_CONTEXT Context
+ )
+{
+ EFI_STATUS Status;
+ BOOLEAN State;
+ UINT8 Value;
+
+ //
+ // First, we disable the variable policy
+ //
+ Status = mVarPol->DisableVariablePolicy ();
+ UT_ASSERT_NOT_EFI_ERROR (Status);
+
+ //
+ // Confirm it's disabled
+ //
+ Status = mVarPol->IsVariablePolicyEnabled (&State);
+ UT_ASSERT_NOT_EFI_ERROR (Status);
+ UT_ASSERT_EQUAL (State, FALSE);
+
+ //
+ // Try locking it?
+ //
+ Status = mVarPol->LockVariablePolicy ();
+ UT_LOG_INFO ("Locking VP after disabling it status: %r\n", Status);
+
+ //
+ // Try modifying the var from TestExistingVarLockNow
+ //
+ Value = 0xB5;
+ Status = gRT->SetVariable (L"ExistingLockNowVar",
+ &mTestNamespaceGuid1,
+ (EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_NON_VOLATILE),
+ sizeof (Value),
+ &Value);
+ UT_ASSERT_NOT_EFI_ERROR (Status);
+
+ return UNIT_TEST_PASSED;
+} // DisablePolicyEngineTests
+
+//
+// Pre-Disable Setup and Test for Authenticated Variables
+//
+UNIT_TEST_STATUS
+TestAuthVarPart1 (
+ IN UNIT_TEST_CONTEXT Context
+ )
+{
+ EFI_STATUS Status;
+ UINT32 Data;
+ UINTN DataSize;
+ UINT8 *DeleteData;
+
+ // First, we need to create our dummy Authenticated Variable.
+ Status = gRT->SetVariable(TEST_AUTH_VAR_NAME,
+ &mTestAuthNamespaceGuid,
+ (EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS |
+ EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS),
+ mTestAuthVarPayloadSize,
+ &mTestAuthVarPayload[0]);
+ UT_ASSERT_NOT_EFI_ERROR(Status);
+
+ // Prove that we created it.
+ DataSize = sizeof(Data);
+ Status = gRT->GetVariable(TEST_AUTH_VAR_NAME,
+ &mTestAuthNamespaceGuid,
+ NULL,
+ &DataSize,
+ &Data);
+ UT_ASSERT_NOT_EFI_ERROR(Status);
+ UT_ASSERT_EQUAL(Data, 0xDEADBEEF);
+
+ // Prove that we cannot delete it.
+ DeleteData = NULL;
+ DataSize = 0;
+ Status = CreateEmptyTimeBasedPayload(&DataSize, &DeleteData, NULL);
+ UT_ASSERT_NOT_EFI_ERROR(Status);
+ Status = gRT->SetVariable(TEST_AUTH_VAR_NAME,
+ &mTestAuthNamespaceGuid,
+ (EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS |
+ EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS),
+ DataSize,
+ (VOID*)DeleteData);
+ UT_ASSERT_STATUS_EQUAL(Status, EFI_SECURITY_VIOLATION);
+ FreePool(DeleteData);
+
+ return UNIT_TEST_PASSED;
+}
+
+//
+// Post-Disable Test for Authenticated Variables
+//
+UNIT_TEST_STATUS
+TestAuthVarPart2 (
+ IN UNIT_TEST_CONTEXT Context
+ )
+{
+ EFI_STATUS Status;
+ UINT32 Data;
+ UINTN DataSize;
+ UINT8 *DeleteData;
+
+ // Prove that it exists.
+ DataSize = sizeof(Data);
+ Status = gRT->GetVariable(TEST_AUTH_VAR_NAME,
+ &mTestAuthNamespaceGuid,
+ NULL,
+ &DataSize,
+ &Data);
+ UT_ASSERT_NOT_EFI_ERROR(Status);
+ UT_ASSERT_EQUAL(Data, 0xDEADBEEF);
+
+ // Prove that we can delete it.
+ DeleteData = NULL;
+ DataSize = 0;
+ Status = CreateEmptyTimeBasedPayload(&DataSize, &DeleteData, NULL);
+ UT_ASSERT_NOT_EFI_ERROR(Status);
+ Status = gRT->SetVariable(TEST_AUTH_VAR_NAME,
+ &mTestAuthNamespaceGuid,
+ (EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS |
+ EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS),
+ DataSize,
+ (VOID*)DeleteData);
+ UT_ASSERT_NOT_EFI_ERROR(Status);
+ FreePool(DeleteData);
+
+ // Prove that we deleted it.
+ DataSize = sizeof(Data);
+ Status = gRT->GetVariable(TEST_AUTH_VAR_NAME,
+ &mTestAuthNamespaceGuid,
+ NULL,
+ &DataSize,
+ &Data);
+ UT_ASSERT_STATUS_EQUAL(Status, EFI_NOT_FOUND);
+
+ return UNIT_TEST_PASSED;
+}
+
+/**
+ Final Cleanup: delete some variables earlier test cases created.
+**/
+STATIC
+VOID
+EFIAPI
+FinalCleanup (
+ IN UNIT_TEST_CONTEXT Context
+ )
+{
+ EFI_STATUS Status;
+
+ Status = gRT->SetVariable (L"ExistingLockNowVar",
+ &mTestNamespaceGuid1,
+ 0,
+ 0,
+ NULL);
+ UT_LOG_INFO ("Delete ExistingLockNowVar status: %r\n", Status);
+
+ Status = gRT->SetVariable (L"ExistingLockOnCreateVar",
+ &mTestNamespaceGuid1,
+ 0,
+ 0,
+ NULL);
+ UT_LOG_INFO ("Delete ExistingLockOnCreateVar status: %r\n", Status);
+
+ Status = gRT->SetVariable (L"NonexistentLockOnCreateVar",
+ &mTestNamespaceGuid1,
+ 0,
+ 0,
+ NULL);
+ UT_LOG_INFO ("Delete NonexistentLockOnCreateVar status: %r\n", Status);
+
+ Status = gRT->SetVariable (L"NonexistentLockNowVar",
+ &mTestNamespaceGuid1,
+ 0,
+ 0,
+ NULL);
+ UT_LOG_INFO ("Delete NonexistentLockNowVar status: %r\n", Status);
+
+ Status = gRT->SetVariable (L"CantHaveAttrNoLockVar",
+ &mTestNamespaceGuid1,
+ 0,
+ 0,
+ NULL);
+ UT_LOG_INFO ("Delete CantHaveAttrNoLockVar status: %r\n", Status);
+
+ Status = gRT->SetVariable (L"NonexistentLockOnVarStateVar",
+ &mTestNamespaceGuid1,
+ 0,
+ 0,
+ NULL);
+ UT_LOG_INFO ("Delete NonexistentLockOnVarStateVar status: %r\n", Status);
+
+ Status = gRT->SetVariable (L"ExistingLockOnVarStateVar",
+ &mTestNamespaceGuid1,
+ 0,
+ 0,
+ NULL);
+ UT_LOG_INFO ("Delete ExistingLockOnVarStateVar status: %r\n", Status);
+} // FinalCleanup
+
+/**
+
+ Main fuction sets up the unit test environment.
+
+**/
+EFI_STATUS
+EFIAPI
+UefiMain (
+ IN EFI_HANDLE ImageHandle,
+ IN EFI_SYSTEM_TABLE* SystemTable)
+{
+ EFI_STATUS Status;
+ UNIT_TEST_FRAMEWORK_HANDLE Framework;
+ UNIT_TEST_SUITE_HANDLE GettingStartedTestSuite;
+ UNIT_TEST_SUITE_HANDLE NoLockPoliciesTestSuite;
+ UNIT_TEST_SUITE_HANDLE LockNowPoliciesTestSuite;
+ UNIT_TEST_SUITE_HANDLE LockOnCreatePoliciesTestSuite;
+ UNIT_TEST_SUITE_HANDLE LockOnVarStatePoliciesTestSuite;
+ UNIT_TEST_SUITE_HANDLE InvalidPoliciesTestSuite;
+ UNIT_TEST_SUITE_HANDLE DumpPolicyTestSuite;
+ UNIT_TEST_SUITE_HANDLE PolicyVersionTestSuite;
+ UNIT_TEST_SUITE_HANDLE LockPolicyTestSuite;
+ UNIT_TEST_SUITE_HANDLE DisablePolicyTestSuite;
+
+ Framework = NULL;
+ GettingStartedTestSuite = NULL;
+ NoLockPoliciesTestSuite = NULL;
+ LockNowPoliciesTestSuite = NULL;
+ LockOnCreatePoliciesTestSuite = NULL;
+ LockOnVarStatePoliciesTestSuite = NULL;
+ InvalidPoliciesTestSuite = NULL;
+ DumpPolicyTestSuite = NULL;
+ PolicyVersionTestSuite = NULL;
+ LockPolicyTestSuite = NULL;
+ DisablePolicyTestSuite = NULL;
+
+ DEBUG ((DEBUG_INFO, "%a v%a\n", UNIT_TEST_APP_NAME, UNIT_TEST_APP_VERSION));
+
+ //
+ // Start setting up the test framework for running the tests.
+ //
+ Status = InitUnitTestFramework (&Framework, UNIT_TEST_APP_NAME, gEfiCallerBaseName, UNIT_TEST_APP_VERSION);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "Failed in InitUnitTestFramework. Status = %r\n", Status));
+ goto EXIT;
+ }
+
+ //
+ // Test suite 1: Getting Started. Get VP protocol, check state, log revision
+ //
+ Status = CreateUnitTestSuite (&GettingStartedTestSuite, Framework, "Getting Started", "Common.VP.GettingStarted", NULL, NULL);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "Failed in CreateUnitTestSuite for the Getting Started Test Suite\n"));
+ Status = EFI_OUT_OF_RESOURCES;
+ goto EXIT;
+ }
+ AddTestCase (GettingStartedTestSuite, "Confirm VP is enabled", "Common.VP.GettingStarted.CheckVpEnabled", CheckVpEnabled, LocateVarPolicyPreReq, NULL, NULL);
+ AddTestCase (GettingStartedTestSuite, "Check VP revision", "Common.VP.GettingStarted.CheckVpRevision", CheckVpRevision, LocateVarPolicyPreReq, NULL, NULL);
+
+ //
+ // Test suite 2: Test NoLock Policies
+ //
+ Status = CreateUnitTestSuite (&NoLockPoliciesTestSuite, Framework, "Exercise NoLock Policies", "Common.VP.NoLockPolicies", NULL, NULL);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "Failed in CreateUnitTestSuite for the NoLock Policies Test Suite\n"));
+ Status = EFI_OUT_OF_RESOURCES;
+ goto EXIT;
+ }
+ AddTestCase (NoLockPoliciesTestSuite, "Test Min Size enforcement in NoLock policy", "Common.VP.NoLockPolicies.TestMinSizeNoLock", TestMinSizeNoLock, LocateVarPolicyPreReq, NULL, NULL);
+ AddTestCase (NoLockPoliciesTestSuite, "Test Max Size enforcement in NoLock policy", "Common.VP.NoLockPolicies.TestMaxSizeNoLock", TestMaxSizeNoLock, LocateVarPolicyPreReq, NULL, NULL);
+ AddTestCase (NoLockPoliciesTestSuite, "Test Must Have Attribute enforcement in NoLock policy", "Common.VP.NoLockPolicies.TestMustHaveAttrNoLock", TestMustHaveAttrNoLock, LocateVarPolicyPreReq, NULL, NULL);
+ AddTestCase (NoLockPoliciesTestSuite, "Test Can't Have Attribute enforcement in NoLock policy", "Common.VP.NoLockPolicies.TestCantHaveAttrNoLock", TestCantHaveAttrNoLock, LocateVarPolicyPreReq, NULL, NULL);
+ AddTestCase (NoLockPoliciesTestSuite, "Test Max Size enforcement in NoLock policy for entire namespace", "Common.VP.NoLockPolicies.TestMaxSizeNamespaceNoLock", TestMaxSizeNamespaceNoLock, LocateVarPolicyPreReq, NULL, NULL);
+ AddTestCase (NoLockPoliciesTestSuite, "Test Must Have Attribute enforcement in NoLock policy with wildcards", "Common.VP.NoLockPolicies.TestMustHaveAttrWildcardNoLock", TestMustHaveAttrWildcardNoLock, LocateVarPolicyPreReq, NULL, NULL);
+ AddTestCase (NoLockPoliciesTestSuite, "Test policy prioritization between namespace-wide, wildcard, and var-specific policies", "Common.VP.NoLockPolicies.TestPolicyprioritizationNoLock", TestPolicyprioritizationNoLock, LocateVarPolicyPreReq, NULL, NULL);
+
+ //
+ // Test suite 3: Test LockNow policies
+ //
+ Status = CreateUnitTestSuite (&LockNowPoliciesTestSuite, Framework, "Exercise LockNow Policies", "Common.VP.LockNowPolicies", NULL, NULL);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "Failed in CreateUnitTestSuite for the LockNow Policies Test Suite\n"));
+ Status = EFI_OUT_OF_RESOURCES;
+ goto EXIT;
+ }
+ AddTestCase (LockNowPoliciesTestSuite, "Test LockNow policy for a pre-existing variable", "Common.VP.LockNowPolicies.TestExistingVarLockNow", TestExistingVarLockNow, LocateVarPolicyPreReq, NULL, NULL);
+ AddTestCase (LockNowPoliciesTestSuite, "Test LockNow policy for a nonexistent variable", "Common.VP.LockNowPolicies.TestNonexistentVarLockNow", TestNonexistentVarLockNow, LocateVarPolicyPreReq, NULL, NULL);
+
+ //
+ // Test suite 4: Test LockOnCreate policies
+ //
+ Status = CreateUnitTestSuite (&LockOnCreatePoliciesTestSuite, Framework, "Exercise LockOnCreate Policies", "Common.VP.LockOnCreate", NULL, NULL);
+ if (EFI_ERROR (Status))
+ {
+ DEBUG ((DEBUG_ERROR, "Failed in CreateUnitTestSuite for the LockOnCreate Policies Test Suite\n"));
+ Status = EFI_OUT_OF_RESOURCES;
+ goto EXIT;
+ }
+ AddTestCase (LockOnCreatePoliciesTestSuite, "Test LockOnCreate policy for a pre-existing variable", "Common.VP.LockOnCreate.TestExistingVarLockOnCreate", TestExistingVarLockOnCreate, LocateVarPolicyPreReq, NULL, NULL);
+ AddTestCase (LockOnCreatePoliciesTestSuite, "Test LockOnCreate policy for a nonexistent variable", "Common.VP.LockOnCreate.TestNonexistentVarLockOnCreate", TestNonexistentVarLockOnCreate, LocateVarPolicyPreReq, NULL, NULL);
+
+ //
+ // Test suite 5: Test LockOnVarState policies
+ //
+ Status = CreateUnitTestSuite (&LockOnVarStatePoliciesTestSuite, Framework, "Exercise LockOnVarState Policies", "Common.VP.LockOnVarState", NULL, NULL);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "Failed in CreateUnitTestSuite for the LockOnVarState Policies Test Suite\n"));
+ Status = EFI_OUT_OF_RESOURCES;
+ goto EXIT;
+ }
+ AddTestCase (LockOnVarStatePoliciesTestSuite, "Test LockOnVarState policy for a nonexistent variable", "Common.VP.LockOnVarState.TestLockOnVarStateBeforeCreate", TestLockOnVarStateBeforeCreate, LocateVarPolicyPreReq, NULL, NULL);
+ AddTestCase (LockOnVarStatePoliciesTestSuite, "Test LockOnVarState policy for a pre-existing variable", "Common.VP.LockOnVarState.TestLockOnVarStateAfterCreate", TestLockOnVarStateAfterCreate, LocateVarPolicyPreReq, NULL, NULL);
+ AddTestCase (LockOnVarStatePoliciesTestSuite, "Test LockOnVarState policy triggered by invalid-size variable", "Common.VP.LockOnVarState.TestLockOnVarStateInvalidLargeTrigger", TestLockOnVarStateInvalidLargeTrigger, LocateVarPolicyPreReq, NULL, NULL);
+ AddTestCase (LockOnVarStatePoliciesTestSuite, "Test LockOnVarState policy triggered by invalid-value variable", "Common.VP.LockOnVarState.TestLockOnVarStateWrongValueTrigger", TestLockOnVarStateWrongValueTrigger, LocateVarPolicyPreReq, NULL, NULL);
+
+ //
+ // Test suite 6: Test registering invalid policies
+ //
+ Status = CreateUnitTestSuite (&InvalidPoliciesTestSuite, Framework, "Attempt registering invalid policies", "Common.VP.InvalidPolicies", NULL, NULL);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "Failed in CreateUnitTestSuite for the Invalid Policies Test Suite\n"));
+ Status = EFI_OUT_OF_RESOURCES;
+ goto EXIT;
+ }
+ AddTestCase (InvalidPoliciesTestSuite, "Test policy with invalid must-have attributes", "Common.VP.InvalidPolicies.TestInvalidAttributesPolicy", TestInvalidAttributesPolicy, LocateVarPolicyPreReq, NULL, NULL);
+ AddTestCase (InvalidPoliciesTestSuite, "Test policy with invalid attributes", "Common.VP.InvalidPolicies.TestLargeMinSizePolicy", TestLargeMinSizePolicy, LocateVarPolicyPreReq, NULL, NULL);
+ AddTestCase (InvalidPoliciesTestSuite, "Test policy with invalid attributes", "Common.VP.InvalidPolicies.TestZeroMaxSizePolicy", TestZeroMaxSizePolicy, LocateVarPolicyPreReq, NULL, NULL);
+ AddTestCase (InvalidPoliciesTestSuite, "Test policy with invalid type", "Common.VP.InvalidPolicies.TestInvalidPolicyTypePolicy", TestInvalidPolicyTypePolicy, LocateVarPolicyPreReq, NULL, NULL);
+
+ //
+ // Test suite 7: Test dumping the policy
+ //
+ Status = CreateUnitTestSuite (&DumpPolicyTestSuite, Framework, "Attempt dumping policy", "Common.VP.DumpPolicy", NULL, NULL);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "Failed in CreateUnitTestSuite for the Dump Policy Test Suite\n"));
+ Status = EFI_OUT_OF_RESOURCES;
+ goto EXIT;
+ }
+ AddTestCase (DumpPolicyTestSuite, "Test dumping policy", "Common.VP.DumpPolicy.TestDumpPolicy", TestDumpPolicy, LocateVarPolicyPreReq, NULL, NULL);
+
+ //
+ // Test suite 8: Test policy version
+ //
+ Status = CreateUnitTestSuite (&PolicyVersionTestSuite, Framework, "Use non-zero policy version", "Common.VP.PolicyVersion", NULL, NULL);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "Failed in CreateUnitTestSuite for the Policy Version Test Suite\n"));
+ Status = EFI_OUT_OF_RESOURCES;
+ goto EXIT;
+ }
+ AddTestCase (PolicyVersionTestSuite, "Test policy version", "Common.VP.DumpPolicy.TestPolicyVersion", TestPolicyVersion, LocateVarPolicyPreReq, NULL, NULL);
+
+ //
+ // Test suite 9: Lock VPE and test implications
+ //
+ Status = CreateUnitTestSuite (&LockPolicyTestSuite, Framework, "Lock policy, test it", "Common.VP.LockPolicyTests", NULL, NULL);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "Failed in CreateUnitTestSuite for the Lock Policy Test Suite\n"));
+ Status = EFI_OUT_OF_RESOURCES;
+ goto EXIT;
+ }
+ AddTestCase (LockPolicyTestSuite, "Test locking policy", "Common.VP.LockPolicyTests.LockPolicyEngineTests", LockPolicyEngineTests, LocateVarPolicyPreReq, NULL, NULL);
+ AddTestCase (LockPolicyTestSuite, "Test locking policy", "Common.VP.LockPolicyTests.LockPolicyEngineTests", LockPolicyEngineTests, LocateVarPolicyPreReq, IgnoreContextAndReboot, NULL);
+
+ //
+ // Test suite 10: Disable var policy and confirm expected behavior
+ //
+ Status = CreateUnitTestSuite (&DisablePolicyTestSuite, Framework, "Disable policy, test it", "Common.VP.DisablePolicyTests", NULL, NULL);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "Failed in CreateUnitTestSuite for the Disable Policy Test Suite\n"));
+ Status = EFI_OUT_OF_RESOURCES;
+ goto EXIT;
+ }
+ AddTestCase (DisablePolicyTestSuite, "Confirm VP is enabled", "Common.VP.DisablePolicyTests.CheckVpEnabled", CheckVpEnabled, LocateVarPolicyPreReq, NULL, NULL);
+ AddTestCase (DisablePolicyTestSuite, "Test LockNow policy for a pre-existing variable", "Common.VP.DisablePolicyTests.TestExistingVarLockNow", TestExistingVarLockNow, LocateVarPolicyPreReq, NULL, NULL);
+ AddTestCase (DisablePolicyTestSuite, "Test AuthVar protection while VariablePolicy is enabled", "Common.VP.DisablePolicyTests.TestAuthVar1", TestAuthVarPart1, VarPolicyEnabledPreReq, NULL, NULL);
+ AddTestCase (DisablePolicyTestSuite, "Test disabling policy", "Common.VP.DisablePolicyTests.DisablePolicyEngineTests", DisablePolicyEngineTests, LocateVarPolicyPreReq, NULL, NULL);
+ AddTestCase (DisablePolicyTestSuite, "Test AuthVar protection while VariablePolicy is disabled", "Common.VP.DisablePolicyTests.TestAuthVar2", TestAuthVarPart2, VarPolicyDisabledPreReq, FinalCleanup, NULL);
+
+ //
+ // Execute the tests.
+ //
+ Status = RunAllTestSuites (Framework);
+
+EXIT:
+ if (Framework != NULL) {
+ FreeUnitTestFramework (Framework);
+ }
+
+ return Status;
+} // UefiMain
diff --git a/MdeModulePkg/MdeModulePkg.ci.yaml b/MdeModulePkg/MdeModulePkg.ci.yaml
index 20d53fc5a5fa..a1beee9f4aab 100644
--- a/MdeModulePkg/MdeModulePkg.ci.yaml
+++ b/MdeModulePkg/MdeModulePkg.ci.yaml
@@ -53,7 +53,9 @@
"UnitTestFrameworkPkg/UnitTestFrameworkPkg.dec"
],
# For UEFI shell based apps
- "AcceptableDependencies-UEFI_APPLICATION":[],
+ "AcceptableDependencies-UEFI_APPLICATION":[
+ "UnitTestFrameworkPkg/UnitTestFrameworkPkg.dec"
+ ],
"IgnoreInf": []
},
diff --git a/MdeModulePkg/Test/ShellTest/VariablePolicyFuncTestApp/Readme.md b/MdeModulePkg/Test/ShellTest/VariablePolicyFuncTestApp/Readme.md
new file mode 100644
index 000000000000..804ad4173a5f
--- /dev/null
+++ b/MdeModulePkg/Test/ShellTest/VariablePolicyFuncTestApp/Readme.md
@@ -0,0 +1,55 @@
+# variable Policy Unit Tests
+
+## 🔹 Copyright
+Copyright (C) Microsoft Corporation. All rights reserved.
+SPDX-License-Identifier: BSD-2-Clause-Patent
+
+## About This Test
+This test verifies functionality of the Variable Policy Protocol by registering various variable policies and exercising them, as well as tests locking the policy, disabling it, and dumping the policy entries.
+
+Only policies that are created as a part of this test will be tested.
+1. Try getting test context, if empty then get VP protocol, confirm that VP is not disabled by calling IsVariablePolicyEnabled. Log VP revision.
+2. "No lock" policies:
+ * check minsize enforcement
+ * check maxsize enforcement
+ * check musthave attr enforcement
+ * check canthave attr enforcement
+ * check one of the above with empty string policy i.e. name wildcard
+ * check another one of the above with a "#" containing policy string
+ * check policy prioritization by having a namespace-wide policy, a policy with a # wildcard, and a one-var specific policy and testing which one is enforced
+3. "Lock now" policies (means if the var doesn't exist, it won't be created; if one exists, it can't be updated):
+ * test a policy for an already existing variable, verify we can't write into that variable
+ * create a policy for a non-existing variable and attempt to register such var
+4. "Lock on create" policies (means the var can still be created, but no updates later, existing vars can't be updated):
+ * create a var, lock it with LockOnCreate, attempt to update its contents
+ * create LockOnCreate VP, attempt to create var with invalid size, then invalid attr, then create valid var, attempt to update its contents
+5. "Lock on var state" policies (means the var protected by this policy can't be created or updated once the trigger is set)
+ * create VP, trigger lock with a valid var, attempt to create a locked var, then modify the trigger var, create locked var
+ * create VP, create targeted var, modify it, trigger lock, attempt to modify var
+ * create VP, trigger lock with invalid (larger than one byte) var, see if VPE allows creation of the locked var (it should allow)
+ * create VP, set locking var with wrong value, see if VPE allows creation of the locked var (should allow)
+6. Attempt registering invalid policy entries
+ * invalid required and banned attributes
+ * large min size - let's say 2GB
+ * max size equal to 0
+ * invalid policy type
+7. Exercise dumping policy. No need to check the validity of the dump blob.
+8. Test registering a policy with a random version.
+9. Lock VPE, make sure old policies are enforced, new ones can't be registered.
+ * Register a LockOnCreate policy
+ * Lock VPE
+ * Test locking it again.
+ * Verify one of the prior policies is enforced
+ * Make sure we can create variables even if those are protected by LockOnCreate policy, after locking the VPE
+ * Attempt to register new policies
+ * Make sure can't disable VPE
+ * Cleanup: save context and reboot
+10. Disable variable policy and try some things
+ * Locate Variable Policy Protocol
+ * Make sure VP is enabled
+ * Register a policy
+ * Disable VPE
+ * Call IsVariablePolicyEnabled to confirm it's disabled.
+ * Make sure can't lock policy
+ * Make sure the policy from a is no longer enforced
+ * Final cleanup: delete vars that were created in some earlier test suites
diff --git a/MdeModulePkg/Test/ShellTest/VariablePolicyFuncTestApp/VariablePolicyFuncTestApp.inf b/MdeModulePkg/Test/ShellTest/VariablePolicyFuncTestApp/VariablePolicyFuncTestApp.inf
new file mode 100644
index 000000000000..bfbac406b504
--- /dev/null
+++ b/MdeModulePkg/Test/ShellTest/VariablePolicyFuncTestApp/VariablePolicyFuncTestApp.inf
@@ -0,0 +1,47 @@
+## @file
+# Uefi Shell based Application that unit tests the Variable Policy Protocol
+#
+# Copyright (c) Microsoft Corporation.
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+##
+
+[Defines]
+ INF_VERSION = 0x00010005
+ BASE_NAME = VariablePolicyFuncTestApp
+ FILE_GUID = B653C4C3-3FCC-4B6C-8051-5F692AEAECBA
+ MODULE_TYPE = UEFI_APPLICATION
+ VERSION_STRING = 1.0
+ ENTRY_POINT = UefiMain
+
+#
+# The following information is for reference only and not required by the build tools.
+#
+# VALID_ARCHITECTURES = X64 AARCH64
+#
+
+[Sources]
+ VariablePolicyFuncTestApp.c
+ VariablePolicyTestAuthVar.h
+
+[Packages]
+ MdePkg/MdePkg.dec
+ MdeModulePkg/MdeModulePkg.dec
+ UnitTestFrameworkPkg/UnitTestFrameworkPkg.dec
+
+[LibraryClasses]
+ UefiApplicationEntryPoint
+ BaseLib
+ BaseMemoryLib
+ UnitTestLib
+ UnitTestBootLib
+ PrintLib
+ UefiBootServicesTableLib
+ UefiRuntimeServicesTableLib
+ MemoryAllocationLib
+ VariablePolicyHelperLib
+
+[Guids]
+ gEfiCertPkcs7Guid
+
+[Protocols]
+ gEdkiiVariablePolicyProtocolGuid
diff --git a/MdeModulePkg/Test/ShellTest/VariablePolicyFuncTestApp/VariablePolicyTestAuthVar.h b/MdeModulePkg/Test/ShellTest/VariablePolicyFuncTestApp/VariablePolicyTestAuthVar.h
new file mode 100644
index 000000000000..c90310226827
--- /dev/null
+++ b/MdeModulePkg/Test/ShellTest/VariablePolicyFuncTestApp/VariablePolicyTestAuthVar.h
@@ -0,0 +1,128 @@
+/** @file -- VarPolicyTestAuthVar.h
+Payload to be used to create an Authenticated Variable for testing.
+
+Copyright (c) Microsoft Corporation.
+SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#ifndef _VAR_POLICY_TEST_AUTH_VAR_H_
+#define _VAR_POLICY_TEST_AUTH_VAR_H_
+
+UINT8 mTestAuthVarPayload[] = {
+ // EFI_VARIABLE_AUTHENTICATION_2
+ // Timestamp
+ 0xE4, 0x07, 0x08, 0x15, 0x0D, 0x1E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ // AuthInfo (WIN_CERTIFICATE_UEFI_GUID)
+ // Hdr (WIN_CERTIFICATE)
+ // dwLength
+ 0x45, 0x05, 0x00, 0x00,
+ // wRevision
+ 0x00, 0x02,
+ // wCertificateType
+ // (WIN_CERT_TYPE_EFI_GUID)
+ 0xF1, 0x0E,
+ // CertType
+ // (gEfiCertPkcs7Guid)
+ 0x9D, 0xD2, 0xAF, 0x4A, 0xDF, 0x68, 0xEE, 0x49, 0x8A, 0xA9, 0x34, 0x7D, 0x37, 0x56, 0x65, 0xA7,
+ // CertData (Packed SignedData Signature)
+ // Digest Buffer Was...
+ // Name (DummyAuthVar)
+ // 44 00 75 00 6D 00 6D 00 79 00 41 00 75 00 74 00 68 00 56 00 61 00 72 00
+ // Vendor Guid (mTestAuthNamespaceGuid)
+ // C6 A2 C5 B6 CE 3E 9B 4B 8C C8 96 D8 D9 CA D3 4E
+ // Attributes (NV + BS + RT, TimeAuth)
+ // 27 00 00 00
+ // Timestamp
+ // E4 07 08 15 0D 1E 00 00 00 00 00 00 00 00 00 00
+ // Data (0xDEADBEEF)
+ // EF BE AD DE
+ 0x30, 0x82, 0x05, 0x29, 0x02, 0x01, 0x01, 0x31, 0x0F, 0x30, 0x0D, 0x06, 0x09, 0x60, 0x86, 0x48,
+ 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05, 0x00, 0x30, 0x0B, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86,
+ 0xF7, 0x0D, 0x01, 0x07, 0x01, 0xA0, 0x82, 0x03, 0x82, 0x30, 0x82, 0x03, 0x7E, 0x30, 0x82, 0x02,
+ 0x66, 0xA0, 0x03, 0x02, 0x01, 0x02, 0x02, 0x10, 0x5A, 0xAE, 0x85, 0xA8, 0x61, 0x6E, 0x80, 0xA3,
+ 0x4D, 0x11, 0x69, 0x06, 0xC3, 0xFE, 0x2D, 0x89, 0x30, 0x0D, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86,
+ 0xF7, 0x0D, 0x01, 0x01, 0x0B, 0x05, 0x00, 0x30, 0x3F, 0x31, 0x3D, 0x30, 0x3B, 0x06, 0x03, 0x55,
+ 0x04, 0x03, 0x1E, 0x34, 0x00, 0x50, 0x00, 0x41, 0x00, 0x4C, 0x00, 0x49, 0x00, 0x4E, 0x00, 0x44,
+ 0x00, 0x52, 0x00, 0x4F, 0x00, 0x4D, 0x00, 0x45, 0x00, 0x5F, 0x00, 0x53, 0x00, 0x65, 0x00, 0x6C,
+ 0x00, 0x66, 0x00, 0x68, 0x00, 0x6F, 0x00, 0x73, 0x00, 0x74, 0x00, 0x5F, 0x00, 0x53, 0x00, 0x69,
+ 0x00, 0x67, 0x00, 0x6E, 0x00, 0x65, 0x00, 0x72, 0x30, 0x20, 0x17, 0x0D, 0x30, 0x30, 0x30, 0x31,
+ 0x30, 0x31, 0x30, 0x37, 0x30, 0x30, 0x30, 0x30, 0x5A, 0x18, 0x0F, 0x32, 0x39, 0x39, 0x39, 0x31,
+ 0x32, 0x33, 0x31, 0x30, 0x37, 0x30, 0x30, 0x30, 0x30, 0x5A, 0x30, 0x3F, 0x31, 0x3D, 0x30, 0x3B,
+ 0x06, 0x03, 0x55, 0x04, 0x03, 0x1E, 0x34, 0x00, 0x50, 0x00, 0x41, 0x00, 0x4C, 0x00, 0x49, 0x00,
+ 0x4E, 0x00, 0x44, 0x00, 0x52, 0x00, 0x4F, 0x00, 0x4D, 0x00, 0x45, 0x00, 0x5F, 0x00, 0x53, 0x00,
+ 0x65, 0x00, 0x6C, 0x00, 0x66, 0x00, 0x68, 0x00, 0x6F, 0x00, 0x73, 0x00, 0x74, 0x00, 0x5F, 0x00,
+ 0x53, 0x00, 0x69, 0x00, 0x67, 0x00, 0x6E, 0x00, 0x65, 0x00, 0x72, 0x30, 0x82, 0x01, 0x22, 0x30,
+ 0x0D, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82,
+ 0x01, 0x0F, 0x00, 0x30, 0x82, 0x01, 0x0A, 0x02, 0x82, 0x01, 0x01, 0x00, 0xC9, 0xA2, 0x80, 0xE7,
+ 0x3A, 0x0B, 0x3E, 0xCF, 0xEE, 0x0E, 0x22, 0x65, 0xF5, 0x03, 0xD2, 0x6A, 0x99, 0xBF, 0x5F, 0x48,
+ 0xF4, 0xC0, 0xD3, 0x19, 0xE7, 0x6B, 0x09, 0xFC, 0x0C, 0xB0, 0x3B, 0x69, 0x3A, 0x07, 0x6F, 0x36,
+ 0x57, 0xF6, 0x63, 0xAF, 0x6B, 0x7B, 0x30, 0x55, 0xD5, 0xE9, 0xF4, 0xDE, 0x89, 0xE3, 0x5F, 0xA1,
+ 0x71, 0x13, 0x3E, 0x84, 0x5D, 0x46, 0x9F, 0x78, 0xA9, 0x5B, 0xA5, 0x46, 0x3B, 0x38, 0x4F, 0x00,
+ 0x06, 0x63, 0x0E, 0x7A, 0x0A, 0x93, 0xE7, 0x36, 0x87, 0xCC, 0x47, 0xBD, 0xFB, 0x0A, 0x5D, 0x45,
+ 0x9C, 0xC4, 0x1B, 0xE6, 0x9E, 0xCB, 0xAB, 0xF9, 0x20, 0x11, 0xEF, 0x03, 0xCA, 0x9F, 0xE9, 0x29,
+ 0x1A, 0x05, 0xF8, 0xB3, 0x46, 0xB0, 0x3D, 0xFD, 0x88, 0x7C, 0x82, 0x0E, 0x3C, 0x6F, 0xEA, 0x5B,
+ 0xFF, 0xA8, 0xA4, 0xE0, 0x40, 0x2B, 0x25, 0xE8, 0x59, 0x46, 0xEE, 0xDB, 0x4B, 0x5F, 0x02, 0xB3,
+ 0x21, 0x33, 0x47, 0x2E, 0xD5, 0x66, 0x79, 0xF3, 0x79, 0x93, 0x18, 0x75, 0x94, 0x4A, 0x01, 0xCF,
+ 0x59, 0x86, 0xF4, 0x8B, 0x35, 0xBD, 0xA4, 0x58, 0xA4, 0x76, 0x89, 0x77, 0x55, 0x55, 0xB1, 0xE4,
+ 0x00, 0x09, 0x78, 0xF3, 0x29, 0x5B, 0xC0, 0xED, 0xD6, 0x68, 0x7E, 0xDB, 0xAA, 0x9F, 0x4E, 0xFE,
+ 0x67, 0x41, 0x4E, 0x6C, 0xC8, 0xDD, 0x52, 0xD6, 0xA5, 0x8A, 0x8A, 0x56, 0x50, 0x51, 0x27, 0x29,
+ 0x2B, 0xD3, 0x1B, 0x4D, 0xCE, 0x93, 0x76, 0x8E, 0x55, 0x53, 0x55, 0x30, 0x10, 0xF5, 0xF9, 0x6C,
+ 0xAE, 0xDA, 0xBA, 0xAC, 0x36, 0x79, 0x11, 0x02, 0xD0, 0x24, 0x07, 0xA6, 0xD1, 0x56, 0xCB, 0xEC,
+ 0x81, 0x29, 0xA8, 0xC1, 0x2E, 0x9D, 0x9B, 0xF9, 0xE9, 0xF4, 0x55, 0x74, 0xA0, 0x52, 0x87, 0x49,
+ 0x4F, 0xAC, 0x71, 0xFF, 0x30, 0x12, 0x24, 0xDD, 0x6D, 0x50, 0x5C, 0x7D, 0x02, 0x03, 0x01, 0x00,
+ 0x01, 0xA3, 0x74, 0x30, 0x72, 0x30, 0x70, 0x06, 0x03, 0x55, 0x1D, 0x01, 0x04, 0x69, 0x30, 0x67,
+ 0x80, 0x10, 0x0E, 0xB2, 0xFB, 0xDC, 0xD5, 0xAB, 0xCC, 0xB4, 0x3B, 0x46, 0x1B, 0x60, 0x18, 0xFD,
+ 0xDE, 0x74, 0xA1, 0x41, 0x30, 0x3F, 0x31, 0x3D, 0x30, 0x3B, 0x06, 0x03, 0x55, 0x04, 0x03, 0x1E,
+ 0x34, 0x00, 0x50, 0x00, 0x41, 0x00, 0x4C, 0x00, 0x49, 0x00, 0x4E, 0x00, 0x44, 0x00, 0x52, 0x00,
+ 0x4F, 0x00, 0x4D, 0x00, 0x45, 0x00, 0x5F, 0x00, 0x53, 0x00, 0x65, 0x00, 0x6C, 0x00, 0x66, 0x00,
+ 0x68, 0x00, 0x6F, 0x00, 0x73, 0x00, 0x74, 0x00, 0x5F, 0x00, 0x53, 0x00, 0x69, 0x00, 0x67, 0x00,
+ 0x6E, 0x00, 0x65, 0x00, 0x72, 0x82, 0x10, 0x5A, 0xAE, 0x85, 0xA8, 0x61, 0x6E, 0x80, 0xA3, 0x4D,
+ 0x11, 0x69, 0x06, 0xC3, 0xFE, 0x2D, 0x89, 0x30, 0x0D, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7,
+ 0x0D, 0x01, 0x01, 0x0B, 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0xB5, 0xA2, 0xD0, 0x1B, 0x70,
+ 0x24, 0xC2, 0xE8, 0x64, 0xCD, 0xF1, 0xE9, 0x97, 0x9E, 0xA7, 0xC1, 0x86, 0x92, 0x06, 0x2F, 0x8F,
+ 0x33, 0x64, 0x0A, 0xB9, 0x2B, 0x77, 0xE2, 0x70, 0x82, 0xDE, 0x06, 0xD3, 0x69, 0x8E, 0xB4, 0x69,
+ 0xF1, 0x6B, 0x59, 0x5E, 0x68, 0x5F, 0xB4, 0xFA, 0x30, 0xC3, 0xB6, 0xA1, 0x72, 0x1A, 0xD4, 0x01,
+ 0xED, 0x69, 0x4A, 0x96, 0x0F, 0x1C, 0xC3, 0x6F, 0x80, 0x0B, 0xE5, 0xD4, 0x46, 0xBE, 0x27, 0x9D,
+ 0xDE, 0x68, 0xB3, 0xA1, 0x93, 0xC3, 0x1A, 0x47, 0x20, 0x7A, 0x87, 0x80, 0x13, 0x85, 0x1E, 0x46,
+ 0x01, 0x42, 0x6A, 0x68, 0x46, 0xE2, 0x77, 0x3D, 0x2E, 0x50, 0xA1, 0x96, 0x23, 0x83, 0x03, 0xD1,
+ 0x57, 0xDD, 0xC6, 0x63, 0x59, 0xB7, 0x1A, 0x49, 0xA2, 0xC9, 0x44, 0x8D, 0xC7, 0x81, 0x18, 0xE8,
+ 0x52, 0x3A, 0x74, 0x32, 0xD3, 0xE6, 0x6D, 0x54, 0x9F, 0xC9, 0x87, 0x1C, 0xBC, 0x81, 0xEB, 0x6D,
+ 0x5D, 0x58, 0xF7, 0x91, 0x81, 0x5B, 0xB0, 0x86, 0xB4, 0x06, 0xE7, 0x19, 0x44, 0xE9, 0x24, 0x28,
+ 0xF5, 0x42, 0x7A, 0x7A, 0x28, 0x94, 0x3E, 0x70, 0x61, 0x1B, 0x68, 0x8D, 0xA9, 0x48, 0x3A, 0xFE,
+ 0x7D, 0xB5, 0x29, 0x10, 0xCE, 0xD6, 0xC1, 0xFF, 0x16, 0xDF, 0x90, 0x94, 0x16, 0xC8, 0xFA, 0x9E,
+ 0x52, 0x49, 0xE5, 0xC3, 0xF5, 0x8C, 0x87, 0xC2, 0x93, 0x3D, 0x3D, 0x27, 0x23, 0x37, 0xC3, 0xDA,
+ 0x55, 0x92, 0x12, 0xE9, 0x1F, 0xEB, 0x32, 0xB5, 0xD8, 0x30, 0xD6, 0xC0, 0x23, 0x45, 0xBB, 0x06,
+ 0xBC, 0x11, 0xA6, 0xA3, 0x47, 0x82, 0x04, 0xCB, 0xAA, 0x98, 0xCA, 0xF9, 0x00, 0x0E, 0xD3, 0xC3,
+ 0x09, 0xF6, 0x21, 0x4C, 0x90, 0xE0, 0x78, 0x08, 0xAE, 0x8F, 0xB1, 0x7D, 0x62, 0x3F, 0x6A, 0x1E,
+ 0xD6, 0xF1, 0x8E, 0xEE, 0xFD, 0x49, 0x04, 0xDE, 0x14, 0x9C, 0x7B, 0x31, 0x82, 0x01, 0x7E, 0x30,
+ 0x82, 0x01, 0x7A, 0x02, 0x01, 0x01, 0x30, 0x53, 0x30, 0x3F, 0x31, 0x3D, 0x30, 0x3B, 0x06, 0x03,
+ 0x55, 0x04, 0x03, 0x1E, 0x34, 0x00, 0x50, 0x00, 0x41, 0x00, 0x4C, 0x00, 0x49, 0x00, 0x4E, 0x00,
+ 0x44, 0x00, 0x52, 0x00, 0x4F, 0x00, 0x4D, 0x00, 0x45, 0x00, 0x5F, 0x00, 0x53, 0x00, 0x65, 0x00,
+ 0x6C, 0x00, 0x66, 0x00, 0x68, 0x00, 0x6F, 0x00, 0x73, 0x00, 0x74, 0x00, 0x5F, 0x00, 0x53, 0x00,
+ 0x69, 0x00, 0x67, 0x00, 0x6E, 0x00, 0x65, 0x00, 0x72, 0x02, 0x10, 0x5A, 0xAE, 0x85, 0xA8, 0x61,
+ 0x6E, 0x80, 0xA3, 0x4D, 0x11, 0x69, 0x06, 0xC3, 0xFE, 0x2D, 0x89, 0x30, 0x0D, 0x06, 0x09, 0x60,
+ 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05, 0x00, 0x30, 0x0D, 0x06, 0x09, 0x2A, 0x86,
+ 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01, 0x05, 0x00, 0x04, 0x82, 0x01, 0x00, 0xA6, 0x06, 0xE7,
+ 0x46, 0x7E, 0xFB, 0x4A, 0xA7, 0x25, 0x2F, 0x52, 0x1D, 0xBC, 0x5C, 0x41, 0x3B, 0xD3, 0x13, 0x50,
+ 0xCE, 0x5F, 0xE2, 0x4B, 0x31, 0xED, 0x28, 0x5E, 0xF5, 0x36, 0xBD, 0x1C, 0x38, 0xA1, 0xB6, 0x45,
+ 0x7C, 0xFD, 0xAB, 0x7B, 0x0C, 0xBF, 0x06, 0x06, 0xBB, 0x95, 0x5E, 0x47, 0x10, 0x7C, 0xD8, 0x10,
+ 0x76, 0x74, 0x81, 0x2D, 0x40, 0x3A, 0xD0, 0xF4, 0x15, 0x9D, 0xDF, 0x44, 0x2B, 0xA4, 0xCD, 0xF7,
+ 0x44, 0x77, 0x9F, 0x35, 0x46, 0xD3, 0x30, 0x67, 0x44, 0x33, 0xF4, 0x7B, 0xB6, 0xC0, 0xE4, 0xA2,
+ 0xAD, 0xDF, 0xAF, 0x56, 0x41, 0xA3, 0x0D, 0x76, 0x36, 0xB9, 0x7E, 0x29, 0x49, 0x17, 0x43, 0xAF,
+ 0xB0, 0xA0, 0xC0, 0xF1, 0xE1, 0xE6, 0xCA, 0x62, 0x9F, 0x3E, 0x9D, 0x6C, 0x63, 0x03, 0xF6, 0xDF,
+ 0x84, 0x32, 0xB1, 0x01, 0x0C, 0x12, 0x83, 0x52, 0x13, 0x2F, 0xAE, 0xBC, 0x79, 0xB7, 0x75, 0xF6,
+ 0x10, 0x20, 0xFC, 0x7A, 0x13, 0x92, 0xF7, 0x87, 0x50, 0xF5, 0x9C, 0xD9, 0xE4, 0xEA, 0x4C, 0x3D,
+ 0x31, 0xED, 0x7F, 0xA6, 0x6C, 0x58, 0xAD, 0x6C, 0x31, 0xAF, 0xC4, 0x64, 0xAE, 0x11, 0xBF, 0x72,
+ 0xF5, 0xAA, 0x69, 0xB4, 0x76, 0xDB, 0x73, 0x8F, 0x8C, 0x3E, 0x23, 0x4A, 0x2D, 0xB7, 0x65, 0x65,
+ 0x10, 0xA8, 0xC6, 0x52, 0x14, 0xE2, 0xC6, 0x2B, 0x07, 0xCE, 0x45, 0x58, 0x6F, 0x92, 0x78, 0xAA,
+ 0xB5, 0xE9, 0x76, 0x39, 0x8A, 0x17, 0xE3, 0x0B, 0xA5, 0x12, 0x0F, 0x2A, 0xC1, 0xCE, 0xC5, 0x4F,
+ 0xD8, 0xA7, 0xD1, 0x7C, 0x3F, 0xE3, 0x23, 0x9B, 0x53, 0x56, 0x18, 0x28, 0x66, 0xC7, 0xB3, 0x04,
+ 0x38, 0xE3, 0x40, 0xCC, 0xB2, 0x18, 0xA8, 0xC7, 0x11, 0xE1, 0x67, 0xD8, 0xBF, 0xBE, 0x8D, 0x2A,
+ 0x75, 0x00, 0x96, 0x8F, 0x7F, 0x80, 0xCF, 0xDB, 0xF0, 0x0D, 0xB5, 0x8D, 0x73,
+ // Data
+ 0xEF, 0xBE, 0xAD, 0xDE
+};
+UINTN mTestAuthVarPayloadSize = sizeof(mTestAuthVarPayload);
+
+#endif // _VAR_POLICY_TEST_AUTH_VAR_H_
--
2.28.0.windows.1
^ permalink raw reply related [flat|nested] 24+ messages in thread