+/** @file -- VariablePolicyLib.c\r
+Business logic for Variable Policy enforcement.\r
+\r
+Copyright (c) Microsoft Corporation.\r
+SPDX-License-Identifier: BSD-2-Clause-Patent\r
+\r
+**/\r
+\r
+#include <Uefi.h>\r
+\r
+#include <Library/SafeIntLib.h>\r
+#include <Library/MemoryAllocationLib.h>\r
+#include <Library/BaseMemoryLib.h>\r
+#include <Library/DebugLib.h>\r
+#include <Library/PcdLib.h>\r
+\r
+#include <Protocol/VariablePolicy.h>\r
+#include <Library/VariablePolicyLib.h>\r
+\r
+\r
+// IMPORTANT NOTE: This library is currently rife with multiple return statements\r
+// for error handling. A refactor should remove these at some point.\r
+\r
+//\r
+// This library was designed with advanced unit-test features.\r
+// This define handles the configuration.\r
+#ifdef INTERNAL_UNIT_TEST\r
+#undef STATIC\r
+#define STATIC // Nothing...\r
+#endif\r
+\r
+// An abstracted GetVariable interface that enables configuration regardless of the environment.\r
+EFI_GET_VARIABLE mGetVariableHelper = NULL;\r
+\r
+// Master switch to lock this entire interface. Does not stop enforcement,\r
+// just prevents the configuration from being changed for the rest of the boot.\r
+STATIC BOOLEAN mInterfaceLocked = FALSE;\r
+\r
+// Master switch to disable the entire interface for a single boot.\r
+// This will disable all policy enforcement for the duration of the boot.\r
+STATIC BOOLEAN mProtectionDisabled = FALSE;\r
+\r
+// Table to hold all the current policies.\r
+UINT8 *mPolicyTable = NULL;\r
+STATIC UINT32 mCurrentTableSize = 0;\r
+STATIC UINT32 mCurrentTableUsage = 0;\r
+STATIC UINT32 mCurrentTableCount = 0;\r
+\r
+#define POLICY_TABLE_STEP_SIZE 0x1000\r
+\r
+// NOTE: DO NOT USE THESE MACROS on any structure that has not been validated.\r
+// Current table data has already been sanitized.\r
+#define GET_NEXT_POLICY(CurPolicy) (VARIABLE_POLICY_ENTRY*)((UINT8*)CurPolicy + CurPolicy->Size)\r
+#define GET_POLICY_NAME(CurPolicy) (CHAR16*)((UINTN)CurPolicy + CurPolicy->OffsetToName)\r
+\r
+#define MATCH_PRIORITY_EXACT 0\r
+#define MATCH_PRIORITY_MAX MATCH_PRIORITY_EXACT\r
+#define MATCH_PRIORITY_MIN MAX_UINT8\r
+\r
+\r
+/**\r
+ An extra init hook that enables the RuntimeDxe library instance to\r
+ register VirtualAddress change callbacks. Among other things.\r
+\r
+ @retval EFI_SUCCESS Everything is good. Continue with init.\r
+ @retval Others Uh... don't continue.\r
+\r
+**/\r
+EFI_STATUS\r
+VariablePolicyExtraInit (\r
+ VOID\r
+ );\r
+\r
+/**\r
+ An extra deinit hook that enables the RuntimeDxe library instance to\r
+ register VirtualAddress change callbacks. Among other things.\r
+\r
+ @retval EFI_SUCCESS Everything is good. Continue with deinit.\r
+ @retval Others Uh... don't continue.\r
+\r
+**/\r
+EFI_STATUS\r
+VariablePolicyExtraDeinit (\r
+ VOID\r
+ );\r
+\r
+\r
+/**\r
+ This helper function determines whether the structure of an incoming policy\r
+ is valid and internally consistent.\r
+\r
+ @param[in] NewPolicy Pointer to the incoming policy structure.\r
+\r
+ @retval TRUE\r
+ @retval FALSE Pointer is NULL, size is wrong, strings are empty, or\r
+ substructures overlap.\r
+\r
+**/\r
+STATIC\r
+BOOLEAN\r
+IsValidVariablePolicyStructure (\r
+ IN CONST VARIABLE_POLICY_ENTRY *NewPolicy\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ UINTN EntryEnd;\r
+ CHAR16 *CheckChar;\r
+ UINTN WildcardCount;\r
+\r
+ // Sanitize some quick values.\r
+ if (NewPolicy == NULL || NewPolicy->Size == 0 ||\r
+ // Structure size should be at least as long as the minumum structure and a NULL string.\r
+ NewPolicy->Size < sizeof(VARIABLE_POLICY_ENTRY) ||\r
+ // Check for the known revision.\r
+ NewPolicy->Version != VARIABLE_POLICY_ENTRY_REVISION) {\r
+ return FALSE;\r
+ }\r
+\r
+ // Calculate the theoretical end of the structure and make sure\r
+ // that the structure can fit in memory.\r
+ Status = SafeUintnAdd( (UINTN)NewPolicy, NewPolicy->Size, &EntryEnd );\r
+ if (EFI_ERROR( Status )) {\r
+ return FALSE;\r
+ }\r
+\r
+ // Check for a valid Max Size.\r
+ if (NewPolicy->MaxSize == 0) {\r
+ return FALSE;\r
+ }\r
+\r
+ // Check for the valid list of lock policies.\r
+ if (NewPolicy->LockPolicyType != VARIABLE_POLICY_TYPE_NO_LOCK &&\r
+ NewPolicy->LockPolicyType != VARIABLE_POLICY_TYPE_LOCK_NOW &&\r
+ NewPolicy->LockPolicyType != VARIABLE_POLICY_TYPE_LOCK_ON_CREATE &&\r
+ NewPolicy->LockPolicyType != VARIABLE_POLICY_TYPE_LOCK_ON_VAR_STATE)\r
+ {\r
+ return FALSE;\r
+ }\r
+\r
+ // If the policy type is VARIABLE_POLICY_TYPE_LOCK_ON_VAR_STATE, make sure that the matching state variable Name\r
+ // terminates before the OffsetToName for the matching policy variable Name.\r
+ if (NewPolicy->LockPolicyType == VARIABLE_POLICY_TYPE_LOCK_ON_VAR_STATE) {\r
+ // Adjust CheckChar to the offset of the LockPolicy->Name.\r
+ Status = SafeUintnAdd( (UINTN)NewPolicy + sizeof(VARIABLE_POLICY_ENTRY),\r
+ sizeof(VARIABLE_LOCK_ON_VAR_STATE_POLICY),\r
+ (UINTN*)&CheckChar );\r
+ if (EFI_ERROR( Status ) || EntryEnd <= (UINTN)CheckChar) {\r
+ return FALSE;\r
+ }\r
+ while (*CheckChar != CHAR_NULL) {\r
+ if (EntryEnd <= (UINTN)CheckChar) {\r
+ return FALSE;\r
+ }\r
+ CheckChar++;\r
+ }\r
+ // At this point we should have either exeeded the structure or be pointing at the last char in LockPolicy->Name.\r
+ // We should check to make sure that the policy Name comes immediately after this charcter.\r
+ if ((UINTN)++CheckChar != (UINTN)NewPolicy + NewPolicy->OffsetToName) {\r
+ return FALSE;\r
+ }\r
+ // If the policy type is any other value, make sure that the LockPolicy structure has a zero length.\r
+ } else {\r
+ if (NewPolicy->OffsetToName != sizeof(VARIABLE_POLICY_ENTRY)) {\r
+ return FALSE;\r
+ }\r
+ }\r
+\r
+ // Check to make sure that the name has a terminating character\r
+ // before the end of the structure.\r
+ // We've already checked that the name is within the bounds of the structure.\r
+ if (NewPolicy->Size != NewPolicy->OffsetToName) {\r
+ CheckChar = (CHAR16*)((UINTN)NewPolicy + NewPolicy->OffsetToName);\r
+ WildcardCount = 0;\r
+ while (*CheckChar != CHAR_NULL) {\r
+ // Make sure there aren't excessive wildcards.\r
+ if (*CheckChar == '#') {\r
+ WildcardCount++;\r
+ if (WildcardCount > MATCH_PRIORITY_MIN) {\r
+ return FALSE;\r
+ }\r
+ }\r
+ // Make sure you're still within the bounds of the policy structure.\r
+ if (EntryEnd <= (UINTN)CheckChar) {\r
+ return FALSE;\r
+ }\r
+ CheckChar++;\r
+ }\r
+\r
+ // Finally, we should be pointed at the very last character in Name, so we should be right\r
+ // up against the end of the structure.\r
+ if ((UINTN)++CheckChar != EntryEnd) {\r
+ return FALSE;\r
+ }\r
+ }\r
+\r
+ return TRUE;\r
+}\r
+\r
+\r
+/**\r
+ This helper function evaluates a policy and determines whether it matches the target\r
+ variable. If matched, will also return a value corresponding to the priority of the match.\r
+\r
+ The rules for "best match" are listed in the Variable Policy Spec.\r
+ Perfect name matches will return 0.\r
+ Single wildcard characters will return the number of wildcard characters.\r
+ Full namespaces will return MAX_UINT8.\r
+\r
+ @param[in] EvalEntry Pointer to the policy entry being evaluated.\r
+ @param[in] VariableName Same as EFI_SET_VARIABLE.\r
+ @param[in] VendorGuid Same as EFI_SET_VARIABLE.\r
+ @param[out] MatchPriority [Optional] On finding a match, this value contains the priority of the match.\r
+ Lower number == higher priority. Only valid if a match found.\r
+\r
+ @retval TRUE Current entry matches the target variable.\r
+ @retval FALSE Current entry does not match at all.\r
+\r
+**/\r
+STATIC\r
+BOOLEAN\r
+EvaluatePolicyMatch (\r
+ IN CONST VARIABLE_POLICY_ENTRY *EvalEntry,\r
+ IN CONST CHAR16 *VariableName,\r
+ IN CONST EFI_GUID *VendorGuid,\r
+ OUT UINT8 *MatchPriority OPTIONAL\r
+ )\r
+{\r
+ BOOLEAN Result;\r
+ CHAR16 *PolicyName;\r
+ UINT8 CalculatedPriority;\r
+ UINTN Index;\r
+\r
+ Result = FALSE;\r
+ CalculatedPriority = MATCH_PRIORITY_EXACT;\r
+\r
+ // Step 1: If the GUID doesn't match, we're done. No need to evaluate anything else.\r
+ if (!CompareGuid( &EvalEntry->Namespace, VendorGuid )) {\r
+ goto Exit;\r
+ }\r
+\r
+ // If the GUID matches, check to see whether there is a Name associated\r
+ // with the policy. If not, this policy matches the entire namespace.\r
+ // Missing Name is indicated by size being equal to name.\r
+ if (EvalEntry->Size == EvalEntry->OffsetToName) {\r
+ CalculatedPriority = MATCH_PRIORITY_MIN;\r
+ Result = TRUE;\r
+ goto Exit;\r
+ }\r
+\r
+ // Now that we know the name exists, get it.\r
+ PolicyName = GET_POLICY_NAME( EvalEntry );\r
+\r
+ // Evaluate the name against the policy name and check for a match.\r
+ // Account for any wildcards.\r
+ Index = 0;\r
+ Result = TRUE;\r
+ // Keep going until the end of both strings.\r
+ while (PolicyName[Index] != CHAR_NULL || VariableName[Index] != CHAR_NULL) {\r
+ // If we don't have a match...\r
+ if (PolicyName[Index] != VariableName[Index] || PolicyName[Index] == '#') {\r
+ // If this is a numerical wildcard, we can consider\r
+ // it a match if we alter the priority.\r
+ if (PolicyName[Index] == L'#' &&\r
+ ((L'0' <= VariableName[Index] && VariableName[Index] <= L'9') ||\r
+ (L'A' <= VariableName[Index] && VariableName[Index] <= L'F') ||\r
+ (L'a' <= VariableName[Index] && VariableName[Index] <= L'f'))) {\r
+ if (CalculatedPriority < MATCH_PRIORITY_MIN) {\r
+ CalculatedPriority++;\r
+ }\r
+ // Otherwise, not a match.\r
+ } else {\r
+ Result = FALSE;\r
+ goto Exit;\r
+ }\r
+ }\r
+ Index++;\r
+ }\r
+\r
+Exit:\r
+ if (Result && MatchPriority != NULL) {\r
+ *MatchPriority = CalculatedPriority;\r
+ }\r
+ return Result;\r
+}\r
+\r
+\r
+/**\r
+ This helper function walks the current policy table and returns a pointer\r
+ to the best match, if any are found. Leverages EvaluatePolicyMatch() to\r
+ determine "best".\r
+\r
+ @param[in] VariableName Same as EFI_SET_VARIABLE.\r
+ @param[in] VendorGuid Same as EFI_SET_VARIABLE.\r
+ @param[out] ReturnPriority [Optional] If pointer is provided, return the\r
+ priority of the match. Same as EvaluatePolicyMatch().\r
+ Only valid if a match is returned.\r
+\r
+ @retval VARIABLE_POLICY_ENTRY* Best match that was found.\r
+ @retval NULL No match was found.\r
+\r
+**/\r
+STATIC\r
+VARIABLE_POLICY_ENTRY*\r
+GetBestPolicyMatch (\r
+ IN CONST CHAR16 *VariableName,\r
+ IN CONST EFI_GUID *VendorGuid,\r
+ OUT UINT8 *ReturnPriority OPTIONAL\r
+ )\r
+{\r
+ VARIABLE_POLICY_ENTRY *BestResult;\r
+ VARIABLE_POLICY_ENTRY *CurrentEntry;\r
+ UINT8 MatchPriority;\r
+ UINT8 CurrentPriority;\r
+ UINTN Index;\r
+\r
+ BestResult = NULL;\r
+ MatchPriority = MATCH_PRIORITY_EXACT;\r
+\r
+ // Walk all entries in the table, looking for matches.\r
+ CurrentEntry = (VARIABLE_POLICY_ENTRY*)mPolicyTable;\r
+ for (Index = 0; Index < mCurrentTableCount; Index++) {\r
+ // Check for a match.\r
+ if (EvaluatePolicyMatch( CurrentEntry, VariableName, VendorGuid, &CurrentPriority )) {\r
+ // If match is better, take it.\r
+ if (BestResult == NULL || CurrentPriority < MatchPriority) {\r
+ BestResult = CurrentEntry;\r
+ MatchPriority = CurrentPriority;\r
+ }\r
+\r
+ // If you've hit the highest-priority match, can exit now.\r
+ if (MatchPriority == 0) {\r
+ break;\r
+ }\r
+ }\r
+\r
+ // If we're still in the loop, move to the next entry.\r
+ CurrentEntry = GET_NEXT_POLICY( CurrentEntry );\r
+ }\r
+\r
+ // If a return priority was requested, return it.\r
+ if (ReturnPriority != NULL) {\r
+ *ReturnPriority = MatchPriority;\r
+ }\r
+\r
+ return BestResult;\r
+}\r
+\r
+\r
+/**\r
+ This API function validates and registers a new policy with\r
+ the policy enforcement engine.\r
+\r
+ @param[in] NewPolicy Pointer to the incoming policy structure.\r
+\r
+ @retval EFI_SUCCESS\r
+ @retval EFI_INVALID_PARAMETER NewPolicy is NULL or is internally inconsistent.\r
+ @retval EFI_ALREADY_STARTED An identical matching policy already exists.\r
+ @retval EFI_WRITE_PROTECTED The interface has been locked until the next reboot.\r
+ @retval EFI_UNSUPPORTED Policy enforcement has been disabled. No reason to add more policies.\r
+ @retval EFI_ABORTED A calculation error has prevented this function from completing.\r
+ @retval EFI_OUT_OF_RESOURCES Cannot grow the table to hold any more policies.\r
+ @retval EFI_NOT_READY Library has not yet been initialized.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+RegisterVariablePolicy (\r
+ IN CONST VARIABLE_POLICY_ENTRY *NewPolicy\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ VARIABLE_POLICY_ENTRY *MatchPolicy;\r
+ UINT8 MatchPriority;\r
+ UINT32 NewSize;\r
+ UINT8 *NewTable;\r
+\r
+ if (!IsVariablePolicyLibInitialized()) {\r
+ return EFI_NOT_READY;\r
+ }\r
+ if (mInterfaceLocked) {\r
+ return EFI_WRITE_PROTECTED;\r
+ }\r
+\r
+ if (!IsValidVariablePolicyStructure( NewPolicy )) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ // Check to see whether an exact matching policy already exists.\r
+ MatchPolicy = GetBestPolicyMatch( GET_POLICY_NAME( NewPolicy ),\r
+ &NewPolicy->Namespace,\r
+ &MatchPriority );\r
+ if (MatchPolicy != NULL && MatchPriority == MATCH_PRIORITY_EXACT) {\r
+ return EFI_ALREADY_STARTED;\r
+ }\r
+\r
+ // If none exists, create it.\r
+ // If we need more space, allocate that now.\r
+ Status = SafeUint32Add( mCurrentTableUsage, NewPolicy->Size, &NewSize );\r
+ if (EFI_ERROR( Status )) {\r
+ return EFI_ABORTED;\r
+ }\r
+ if (NewSize > mCurrentTableSize) {\r
+ // Use NewSize to calculate the new table size in units of POLICY_TABLE_STEP_SIZE.\r
+ NewSize = (NewSize % POLICY_TABLE_STEP_SIZE) > 0 ?\r
+ (NewSize / POLICY_TABLE_STEP_SIZE) + 1 :\r
+ (NewSize / POLICY_TABLE_STEP_SIZE);\r
+ // Calculate the new table size in absolute bytes.\r
+ Status = SafeUint32Mult( NewSize, POLICY_TABLE_STEP_SIZE, &NewSize );\r
+ if (EFI_ERROR( Status )) {\r
+ return EFI_ABORTED;\r
+ }\r
+\r
+ // Reallocate and copy the table.\r
+ NewTable = AllocatePool( NewSize );\r
+ if (NewTable == NULL) {\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+ CopyMem( NewTable, mPolicyTable, mCurrentTableUsage );\r
+ mCurrentTableSize = NewSize;\r
+ if (mPolicyTable != NULL) {\r
+ FreePool( mPolicyTable );\r
+ }\r
+ mPolicyTable = NewTable;\r
+ }\r
+ // Copy the policy into the table.\r
+ CopyMem( mPolicyTable + mCurrentTableUsage, NewPolicy, NewPolicy->Size );\r
+ mCurrentTableUsage += NewPolicy->Size;\r
+ mCurrentTableCount += 1;\r
+\r
+ // We're done here.\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+\r
+/**\r
+ This API function checks to see whether the parameters to SetVariable would\r
+ be allowed according to the current variable policies.\r
+\r
+ @param[in] VariableName Same as EFI_SET_VARIABLE.\r
+ @param[in] VendorGuid Same as EFI_SET_VARIABLE.\r
+ @param[in] Attributes Same as EFI_SET_VARIABLE.\r
+ @param[in] DataSize Same as EFI_SET_VARIABLE.\r
+ @param[in] Data Same as EFI_SET_VARIABLE.\r
+\r
+ @retval EFI_SUCCESS A matching policy allows this update.\r
+ @retval EFI_SUCCESS There are currently no policies that restrict this update.\r
+ @retval EFI_SUCCESS The protections have been disable until the next reboot.\r
+ @retval EFI_WRITE_PROTECTED Variable is currently locked.\r
+ @retval EFI_INVALID_PARAMETER Attributes or size are invalid.\r
+ @retval EFI_ABORTED A lock policy exists, but an error prevented evaluation.\r
+ @retval EFI_NOT_READY Library has not been initialized.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+ValidateSetVariable (\r
+ IN CHAR16 *VariableName,\r
+ IN EFI_GUID *VendorGuid,\r
+ IN UINT32 Attributes,\r
+ IN UINTN DataSize,\r
+ IN VOID *Data\r
+ )\r
+{\r
+ BOOLEAN IsDel;\r
+ VARIABLE_POLICY_ENTRY *ActivePolicy;\r
+ EFI_STATUS Status;\r
+ EFI_STATUS ReturnStatus;\r
+ VARIABLE_LOCK_ON_VAR_STATE_POLICY *StateVarPolicy;\r
+ CHAR16 *StateVarName;\r
+ UINTN StateVarSize;\r
+ UINT8 StateVar;\r
+\r
+ ReturnStatus = EFI_SUCCESS;\r
+\r
+ if (!IsVariablePolicyLibInitialized()) {\r
+ ReturnStatus = EFI_NOT_READY;\r
+ goto Exit;\r
+ }\r
+\r
+ // Bail if the protections are currently disabled.\r
+ if (mProtectionDisabled) {\r
+ ReturnStatus = EFI_SUCCESS;\r
+ goto Exit;\r
+ }\r
+\r
+ // Determine whether this is a delete operation.\r
+ // If so, it will affect which tests are applied.\r
+ if ((DataSize == 0) && ((Attributes & EFI_VARIABLE_APPEND_WRITE) == 0)) {\r
+ IsDel = TRUE;\r
+ } else {\r
+ IsDel = FALSE;\r
+ }\r
+\r
+ // Find an active policy if one exists.\r
+ ActivePolicy = GetBestPolicyMatch( VariableName, VendorGuid, NULL );\r
+\r
+ // If we have an active policy, check it against the incoming data.\r
+ if (ActivePolicy != NULL) {\r
+ //\r
+ // Only enforce size and attribute constraints when updating data, not deleting.\r
+ if (!IsDel) {\r
+ // Check for size constraints.\r
+ if ((ActivePolicy->MinSize > 0 && DataSize < ActivePolicy->MinSize) ||\r
+ (ActivePolicy->MaxSize > 0 && DataSize > ActivePolicy->MaxSize)) {\r
+ ReturnStatus = EFI_INVALID_PARAMETER;\r
+ DEBUG(( DEBUG_VERBOSE, "%a - Bad Size. 0x%X <> 0x%X-0x%X\n", __FUNCTION__,\r
+ DataSize, ActivePolicy->MinSize, ActivePolicy->MaxSize ));\r
+ goto Exit;\r
+ }\r
+\r
+ // Check for attribute constraints.\r
+ if ((ActivePolicy->AttributesMustHave & Attributes) != ActivePolicy->AttributesMustHave ||\r
+ (ActivePolicy->AttributesCantHave & Attributes) != 0) {\r
+ ReturnStatus = EFI_INVALID_PARAMETER;\r
+ DEBUG(( DEBUG_VERBOSE, "%a - Bad Attributes. 0x%X <> 0x%X:0x%X\n", __FUNCTION__,\r
+ Attributes, ActivePolicy->AttributesMustHave, ActivePolicy->AttributesCantHave ));\r
+ goto Exit;\r
+ }\r
+ }\r
+\r
+ //\r
+ // Lock policy check.\r
+ //\r
+ // Check for immediate lock.\r
+ if (ActivePolicy->LockPolicyType == VARIABLE_POLICY_TYPE_LOCK_NOW) {\r
+ ReturnStatus = EFI_WRITE_PROTECTED;\r
+ goto Exit;\r
+ // Check for lock on create.\r
+ } else if (ActivePolicy->LockPolicyType == VARIABLE_POLICY_TYPE_LOCK_ON_CREATE) {\r
+ StateVarSize = 0;\r
+ Status = mGetVariableHelper( VariableName,\r
+ VendorGuid,\r
+ NULL,\r
+ &StateVarSize,\r
+ NULL );\r
+ if (Status == EFI_BUFFER_TOO_SMALL) {\r
+ ReturnStatus = EFI_WRITE_PROTECTED;\r
+ goto Exit;\r
+ }\r
+ // Check for lock on state variable.\r
+ } else if (ActivePolicy->LockPolicyType == VARIABLE_POLICY_TYPE_LOCK_ON_VAR_STATE) {\r
+ StateVarPolicy = (VARIABLE_LOCK_ON_VAR_STATE_POLICY*)((UINT8*)ActivePolicy + sizeof(VARIABLE_POLICY_ENTRY));\r
+ StateVarName = (CHAR16*)((UINT8*)StateVarPolicy + sizeof(VARIABLE_LOCK_ON_VAR_STATE_POLICY));\r
+ StateVarSize = sizeof(StateVar);\r
+ Status = mGetVariableHelper( StateVarName,\r
+ &StateVarPolicy->Namespace,\r
+ NULL,\r
+ &StateVarSize,\r
+ &StateVar );\r
+\r
+ // If the variable was found, check the state. If matched, this variable is locked.\r
+ if (!EFI_ERROR( Status )) {\r
+ if (StateVar == StateVarPolicy->Value) {\r
+ ReturnStatus = EFI_WRITE_PROTECTED;\r
+ goto Exit;\r
+ }\r
+ // EFI_NOT_FOUND and EFI_BUFFER_TOO_SMALL indicate that the state doesn't match.\r
+ } else if (Status != EFI_NOT_FOUND && Status != EFI_BUFFER_TOO_SMALL) {\r
+ // We don't know what happened, but it isn't good.\r
+ ReturnStatus = EFI_ABORTED;\r
+ goto Exit;\r
+ }\r
+ }\r
+ }\r
+\r
+Exit:\r
+ DEBUG(( DEBUG_VERBOSE, "%a - Variable (%g:%s) returning %r.\n", __FUNCTION__, VendorGuid, VariableName, ReturnStatus ));\r
+ return ReturnStatus;\r
+}\r
+\r
+\r
+/**\r
+ This API function disables the variable policy enforcement. If it's\r
+ already been called once, will return EFI_ALREADY_STARTED.\r
+\r
+ @retval EFI_SUCCESS\r
+ @retval EFI_ALREADY_STARTED Has already been called once this boot.\r
+ @retval EFI_WRITE_PROTECTED Interface has been locked until reboot.\r
+ @retval EFI_WRITE_PROTECTED Interface option is disabled by platform PCD.\r
+ @retval EFI_NOT_READY Library has not yet been initialized.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+DisableVariablePolicy (\r
+ VOID\r
+ )\r
+{\r
+ if (!IsVariablePolicyLibInitialized()) {\r
+ return EFI_NOT_READY;\r
+ }\r
+ if (mProtectionDisabled) {\r
+ return EFI_ALREADY_STARTED;\r
+ }\r
+ if (mInterfaceLocked) {\r
+ return EFI_WRITE_PROTECTED;\r
+ }\r
+ if (!PcdGetBool (PcdAllowVariablePolicyEnforcementDisable)) {\r
+ return EFI_WRITE_PROTECTED;\r
+ }\r
+ mProtectionDisabled = TRUE;\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+\r
+/**\r
+ This API function will dump the entire contents of the variable policy table.\r
+\r
+ Similar to GetVariable, the first call can be made with a 0 size and it will return\r
+ the size of the buffer required to hold the entire table.\r
+\r
+ @param[out] Policy Pointer to the policy buffer. Can be NULL if Size is 0.\r
+ @param[in,out] Size On input, the size of the output buffer. On output, the size\r
+ of the data returned.\r
+\r
+ @retval EFI_SUCCESS Policy data is in the output buffer and Size has been updated.\r
+ @retval EFI_INVALID_PARAMETER Size is NULL, or Size is non-zero and Policy is NULL.\r
+ @retval EFI_BUFFER_TOO_SMALL Size is insufficient to hold policy. Size updated with required size.\r
+ @retval EFI_NOT_READY Library has not yet been initialized.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+DumpVariablePolicy (\r
+ OUT UINT8 *Policy,\r
+ IN OUT UINT32 *Size\r
+ )\r
+{\r
+ if (!IsVariablePolicyLibInitialized()) {\r
+ return EFI_NOT_READY;\r
+ }\r
+\r
+ // Check the parameters.\r
+ if (Size == NULL || (*Size > 0 && Policy == NULL)) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ // Make sure the size is sufficient to hold the policy table.\r
+ if (*Size < mCurrentTableUsage) {\r
+ *Size = mCurrentTableUsage;\r
+ return EFI_BUFFER_TOO_SMALL;\r
+ }\r
+\r
+ // If we're still here, copy the table and bounce.\r
+ CopyMem( Policy, mPolicyTable, mCurrentTableUsage );\r
+ *Size = mCurrentTableUsage;\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+\r
+/**\r
+ This API function returns whether or not the policy engine is\r
+ currently being enforced.\r
+\r
+ @retval TRUE\r
+ @retval FALSE\r
+ @retval FALSE Library has not yet been initialized.\r
+\r
+**/\r
+BOOLEAN\r
+EFIAPI\r
+IsVariablePolicyEnabled (\r
+ VOID\r
+ )\r
+{\r
+ if (!IsVariablePolicyLibInitialized()) {\r
+ return FALSE;\r
+ }\r
+ return !mProtectionDisabled;\r
+}\r
+\r
+\r
+/**\r
+ This API function locks the interface so that no more policy updates\r
+ can be performed or changes made to the enforcement until the next boot.\r
+\r
+ @retval EFI_SUCCESS\r
+ @retval EFI_NOT_READY Library has not yet been initialized.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+LockVariablePolicy (\r
+ VOID\r
+ )\r
+{\r
+ if (!IsVariablePolicyLibInitialized()) {\r
+ return EFI_NOT_READY;\r
+ }\r
+ if (mInterfaceLocked) {\r
+ return EFI_WRITE_PROTECTED;\r
+ }\r
+ mInterfaceLocked = TRUE;\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+\r
+/**\r
+ This API function returns whether or not the policy interface is locked\r
+ for the remainder of the boot.\r
+\r
+ @retval TRUE\r
+ @retval FALSE\r
+ @retval FALSE Library has not yet been initialized.\r
+\r
+**/\r
+BOOLEAN\r
+EFIAPI\r
+IsVariablePolicyInterfaceLocked (\r
+ VOID\r
+ )\r
+{\r
+ if (!IsVariablePolicyLibInitialized()) {\r
+ return FALSE;\r
+ }\r
+ return mInterfaceLocked;\r
+}\r
+\r
+\r
+/**\r
+ This helper function initializes the library and sets\r
+ up any required internal structures or handlers.\r
+\r
+ Also registers the internal pointer for the GetVariable helper.\r
+\r
+ @param[in] GetVariableHelper A function pointer matching the EFI_GET_VARIABLE prototype that will be used to\r
+ check policy criteria that involve the existence of other variables.\r
+\r
+ @retval EFI_SUCCESS\r
+ @retval EFI_ALREADY_STARTED The initialize function has been called more than once without a call to\r
+ deinitialize.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+InitVariablePolicyLib (\r
+ IN EFI_GET_VARIABLE GetVariableHelper\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+\r
+ Status = EFI_SUCCESS;\r
+\r
+ if (mGetVariableHelper != NULL) {\r
+ return EFI_ALREADY_STARTED;\r
+ }\r
+\r
+ if (!EFI_ERROR( Status )) {\r
+ Status = VariablePolicyExtraInit();\r
+ }\r
+\r
+ if (!EFI_ERROR( Status )) {\r
+ // Save an internal pointer to the GetVariableHelper.\r
+ mGetVariableHelper = GetVariableHelper;\r
+\r
+ // Initialize the global state.\r
+ mInterfaceLocked = FALSE;\r
+ mProtectionDisabled = FALSE;\r
+ mPolicyTable = NULL;\r
+ mCurrentTableSize = 0;\r
+ mCurrentTableUsage = 0;\r
+ mCurrentTableCount = 0;\r
+ }\r
+\r
+ return Status;\r
+}\r
+\r
+\r
+/**\r
+ This helper function returns whether or not the library is currently initialized.\r
+\r
+ @retval TRUE\r
+ @retval FALSE\r
+\r
+**/\r
+BOOLEAN\r
+EFIAPI\r
+IsVariablePolicyLibInitialized (\r
+ VOID\r
+ )\r
+{\r
+ return (mGetVariableHelper != NULL);\r
+}\r
+\r
+\r
+/**\r
+ This helper function tears down the library.\r
+\r
+ Should generally only be used for test harnesses.\r
+\r
+ @retval EFI_SUCCESS\r
+ @retval EFI_NOT_READY Deinitialize was called without first calling initialize.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+DeinitVariablePolicyLib (\r
+ VOID\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+\r
+ Status = EFI_SUCCESS;\r
+\r
+ if (mGetVariableHelper == NULL) {\r
+ return EFI_NOT_READY;\r
+ }\r
+\r
+ if (!EFI_ERROR( Status )) {\r
+ Status = VariablePolicyExtraDeinit();\r
+ }\r
+\r
+ if (!EFI_ERROR( Status )) {\r
+ mGetVariableHelper = NULL;\r
+ mInterfaceLocked = FALSE;\r
+ mProtectionDisabled = FALSE;\r
+ mCurrentTableSize = 0;\r
+ mCurrentTableUsage = 0;\r
+ mCurrentTableCount = 0;\r
+\r
+ if (mPolicyTable != NULL) {\r
+ FreePool( mPolicyTable );\r
+ mPolicyTable = NULL;\r
+ }\r
+ }\r
+\r
+ return Status;\r
+}\r