summaryrefslogtreecommitdiff
path: root/MdeModulePkg/Library/VarCheckPolicyLib/VarCheckPolicyLib.c
diff options
context:
space:
mode:
Diffstat (limited to 'MdeModulePkg/Library/VarCheckPolicyLib/VarCheckPolicyLib.c')
-rw-r--r--MdeModulePkg/Library/VarCheckPolicyLib/VarCheckPolicyLib.c954
1 files changed, 477 insertions, 477 deletions
diff --git a/MdeModulePkg/Library/VarCheckPolicyLib/VarCheckPolicyLib.c b/MdeModulePkg/Library/VarCheckPolicyLib/VarCheckPolicyLib.c
index 1448af8555..22b71ca6e9 100644
--- a/MdeModulePkg/Library/VarCheckPolicyLib/VarCheckPolicyLib.c
+++ b/MdeModulePkg/Library/VarCheckPolicyLib/VarCheckPolicyLib.c
@@ -1,477 +1,477 @@
-/** @file -- VarCheckPolicyLib.c
-This is a NULL library instance that leverages the VarCheck interface
-and the business logic behind the VariablePolicy code to make its decisions.
-
-Copyright (c) Microsoft Corporation.
-SPDX-License-Identifier: BSD-2-Clause-Patent
-
-**/
-
-#include <Library/VarCheckLib.h>
-#include <Library/BaseLib.h>
-#include <Library/DebugLib.h>
-#include <Library/SafeIntLib.h>
-#include <Library/MmServicesTableLib.h>
-#include <Library/BaseMemoryLib.h>
-#include <Library/MemoryAllocationLib.h>
-
-#include <Protocol/MmCommunication.h>
-
-#include <Protocol/VariablePolicy.h>
-#include <Library/VariablePolicyLib.h>
-
-#include <Guid/VarCheckPolicyMmi.h>
-
-#include "VarCheckPolicyLib.h"
-
-// ================================================
-// As a VarCheck library, we're linked into the VariableServices
-// and may not be able to call them indirectly. To get around this,
-// use the internal GetVariable function to query the variable store.
-// ================================================
-EFI_STATUS
-EFIAPI
-VariableServiceGetVariable (
- IN CHAR16 *VariableName,
- IN EFI_GUID *VendorGuid,
- OUT UINT32 *Attributes OPTIONAL,
- IN OUT UINTN *DataSize,
- OUT VOID *Data
- );
-
-UINT8 mSecurityEvalBuffer[VAR_CHECK_POLICY_MM_COMM_BUFFER_SIZE];
-
-// Pagination Cache Variables
-UINT8 *mPaginationCache = NULL;
-UINTN mPaginationCacheSize = 0;
-UINT32 mCurrentPaginationCommand = 0;
-
-/**
- MM Communication Handler to recieve commands from the DXE protocol for
- Variable Policies. This communication channel is used to register new policies
- and poll and toggle the enforcement of variable policies.
-
- @param[in] DispatchHandle All parameters standard to MM communications convention.
- @param[in] RegisterContext All parameters standard to MM communications convention.
- @param[in,out] CommBuffer All parameters standard to MM communications convention.
- @param[in,out] CommBufferSize All parameters standard to MM communications convention.
-
- @retval EFI_SUCCESS
- @retval EFI_INVALID_PARAMETER CommBuffer or CommBufferSize is null pointer.
- @retval EFI_INVALID_PARAMETER CommBuffer size is wrong.
- @retval EFI_INVALID_PARAMETER Revision or signature don't match.
-
-**/
-STATIC
-EFI_STATUS
-EFIAPI
-VarCheckPolicyLibMmiHandler (
- IN EFI_HANDLE DispatchHandle,
- IN CONST VOID *RegisterContext,
- IN OUT VOID *CommBuffer,
- IN OUT UINTN *CommBufferSize
- )
-{
- UINTN InternalCommBufferSize;
- VOID *InternalCommBuffer;
- EFI_STATUS Status;
- EFI_STATUS SubCommandStatus;
- VAR_CHECK_POLICY_COMM_HEADER *PolicyCommHeader;
- VAR_CHECK_POLICY_COMM_HEADER *InternalPolicyCommHeader;
- VAR_CHECK_POLICY_COMM_IS_ENABLED_PARAMS *IsEnabledParams;
- VAR_CHECK_POLICY_COMM_DUMP_PARAMS *DumpParamsIn;
- VAR_CHECK_POLICY_COMM_DUMP_PARAMS *DumpParamsOut;
- VAR_CHECK_POLICY_COMM_GET_INFO_PARAMS *GetInfoParamsInternal;
- VAR_CHECK_POLICY_COMM_GET_INFO_PARAMS *GetInfoParamsExternal;
- CHAR16 *InternalCopyOfOutputVariableName;
- CHAR16 *ExternalCopyOfOutputVariableName;
- UINT8 *DumpInputBuffer;
- UINT8 *DumpOutputBuffer;
- UINTN AllowedOutputVariableNameSize;
- UINTN DumpTotalPages;
- UINTN LocalSize;
- VARIABLE_POLICY_ENTRY *PolicyEntry;
- UINTN ExpectedSize;
- UINT32 TempSize;
-
- Status = EFI_SUCCESS;
-
- //
- // Validate some input parameters.
- //
- // If either of the pointers are NULL, we can't proceed.
- if ((CommBuffer == NULL) || (CommBufferSize == NULL)) {
- DEBUG ((DEBUG_INFO, "%a - Invalid comm buffer pointers!\n", __func__));
- return EFI_INVALID_PARAMETER;
- }
-
- // Make sure that the buffer does not overlap SMM.
- // This should be covered by the SmiManage infrastructure, but just to be safe...
- InternalCommBufferSize = *CommBufferSize;
- if ((InternalCommBufferSize > VAR_CHECK_POLICY_MM_COMM_BUFFER_SIZE) ||
- !VarCheckPolicyIsBufferOutsideValid ((UINTN)CommBuffer, (UINT64)InternalCommBufferSize))
- {
- DEBUG ((DEBUG_ERROR, "%a - Invalid CommBuffer supplied! 0x%016lX[0x%016lX]\n", __func__, CommBuffer, InternalCommBufferSize));
- return EFI_INVALID_PARAMETER;
- }
-
- // If the size does not meet a minimum threshold, we cannot proceed.
- ExpectedSize = sizeof (VAR_CHECK_POLICY_COMM_HEADER);
- if (InternalCommBufferSize < ExpectedSize) {
- DEBUG ((DEBUG_INFO, "%a - Bad comm buffer size! %d < %d\n", __func__, InternalCommBufferSize, ExpectedSize));
- return EFI_INVALID_PARAMETER;
- }
-
- //
- // Before proceeding any further, copy the buffer internally so that we can compare
- // without worrying about TOCTOU.
- //
- InternalCommBuffer = &mSecurityEvalBuffer[0];
- CopyMem (InternalCommBuffer, CommBuffer, InternalCommBufferSize);
- PolicyCommHeader = CommBuffer;
- InternalPolicyCommHeader = InternalCommBuffer;
- // Check the revision and the signature of the comm header.
- if ((InternalPolicyCommHeader->Signature != VAR_CHECK_POLICY_COMM_SIG) ||
- (InternalPolicyCommHeader->Revision != VAR_CHECK_POLICY_COMM_REVISION))
- {
- DEBUG ((DEBUG_INFO, "%a - Signature or revision are incorrect!\n", __func__));
- // We have verified the buffer is not null and have enough size to hold Result field.
- PolicyCommHeader->Result = EFI_INVALID_PARAMETER;
- return EFI_SUCCESS;
- }
-
- // If we're in the middle of a paginated dump and any other command is sent,
- // pagination cache must be cleared.
- if ((mPaginationCache != NULL) && (InternalPolicyCommHeader->Command != mCurrentPaginationCommand)) {
- FreePool (mPaginationCache);
- mPaginationCache = NULL;
- mPaginationCacheSize = 0;
- mCurrentPaginationCommand = 0;
- }
-
- //
- // Now we can process the command as it was sent.
- //
- PolicyCommHeader->Result = EFI_ABORTED; // Set a default return for incomplete commands.
- switch (InternalPolicyCommHeader->Command) {
- case VAR_CHECK_POLICY_COMMAND_DISABLE:
- PolicyCommHeader->Result = DisableVariablePolicy ();
- break;
-
- case VAR_CHECK_POLICY_COMMAND_IS_ENABLED:
- // Make sure that we're dealing with a reasonable size.
- // This add should be safe because these are fixed sizes so far.
- ExpectedSize += sizeof (VAR_CHECK_POLICY_COMM_IS_ENABLED_PARAMS);
- if (InternalCommBufferSize < ExpectedSize) {
- DEBUG ((DEBUG_INFO, "%a - Bad comm buffer size! %d < %d\n", __func__, InternalCommBufferSize, ExpectedSize));
- PolicyCommHeader->Result = EFI_INVALID_PARAMETER;
- break;
- }
-
- // Now that we know we've got a valid size, we can fill in the rest of the data.
- IsEnabledParams = (VAR_CHECK_POLICY_COMM_IS_ENABLED_PARAMS *)((UINT8 *)CommBuffer + sizeof (VAR_CHECK_POLICY_COMM_HEADER));
- IsEnabledParams->State = IsVariablePolicyEnabled ();
- PolicyCommHeader->Result = EFI_SUCCESS;
- break;
-
- case VAR_CHECK_POLICY_COMMAND_REGISTER:
- // Make sure that we're dealing with a reasonable size.
- // This add should be safe because these are fixed sizes so far.
- ExpectedSize += sizeof (VARIABLE_POLICY_ENTRY);
- if (InternalCommBufferSize < ExpectedSize) {
- DEBUG ((DEBUG_INFO, "%a - Bad comm buffer size! %d < %d\n", __func__, InternalCommBufferSize, ExpectedSize));
- PolicyCommHeader->Result = EFI_INVALID_PARAMETER;
- break;
- }
-
- // At the very least, we can assume that we're working with a valid policy entry.
- // Time to compare its internal size.
- PolicyEntry = (VARIABLE_POLICY_ENTRY *)((UINT8 *)InternalCommBuffer + sizeof (VAR_CHECK_POLICY_COMM_HEADER));
- if ((PolicyEntry->Version != VARIABLE_POLICY_ENTRY_REVISION) ||
- (PolicyEntry->Size < sizeof (VARIABLE_POLICY_ENTRY)) ||
- EFI_ERROR (SafeUintnAdd (sizeof (VAR_CHECK_POLICY_COMM_HEADER), PolicyEntry->Size, &ExpectedSize)) ||
- (InternalCommBufferSize < ExpectedSize))
- {
- DEBUG ((DEBUG_INFO, "%a - Bad policy entry contents!\n", __func__));
- PolicyCommHeader->Result = EFI_INVALID_PARAMETER;
- break;
- }
-
- PolicyCommHeader->Result = RegisterVariablePolicy (PolicyEntry);
- break;
-
- case VAR_CHECK_POLICY_COMMAND_DUMP:
- // Make sure that we're dealing with a reasonable size.
- // This add should be safe because these are fixed sizes so far.
- ExpectedSize += sizeof (VAR_CHECK_POLICY_COMM_DUMP_PARAMS) + VAR_CHECK_POLICY_MM_DUMP_BUFFER_SIZE;
- if (InternalCommBufferSize < ExpectedSize) {
- DEBUG ((DEBUG_INFO, "%a - Bad comm buffer size! %d < %d\n", __func__, InternalCommBufferSize, ExpectedSize));
- PolicyCommHeader->Result = EFI_INVALID_PARAMETER;
- break;
- }
-
- // Now that we know we've got a valid size, we can fill in the rest of the data.
- DumpParamsIn = (VAR_CHECK_POLICY_COMM_DUMP_PARAMS *)(InternalPolicyCommHeader + 1);
- DumpParamsOut = (VAR_CHECK_POLICY_COMM_DUMP_PARAMS *)(PolicyCommHeader + 1);
-
- // If we're requesting the first page, initialize the cache and get the sizes.
- if (DumpParamsIn->PageRequested == 0) {
- if (mPaginationCache != NULL) {
- FreePool (mPaginationCache);
- mPaginationCache = NULL;
- }
-
- // Determine what the required size is going to be.
- DumpParamsOut->TotalSize = 0;
- DumpParamsOut->PageSize = 0;
- DumpParamsOut->HasMore = FALSE;
- TempSize = 0;
- SubCommandStatus = DumpVariablePolicy (NULL, &TempSize);
- if ((SubCommandStatus == EFI_BUFFER_TOO_SMALL) && (TempSize > 0)) {
- mCurrentPaginationCommand = VAR_CHECK_POLICY_COMMAND_DUMP;
- mPaginationCacheSize = TempSize;
- DumpParamsOut->TotalSize = TempSize;
- mPaginationCache = AllocatePool (mPaginationCacheSize);
- if (mPaginationCache == NULL) {
- SubCommandStatus = EFI_OUT_OF_RESOURCES;
- }
- }
-
- // If we've allocated our pagination cache, we're good to cache.
- if (mPaginationCache != NULL) {
- SubCommandStatus = DumpVariablePolicy (mPaginationCache, &TempSize);
- }
-
- // Populate the remaining fields and we can boogie.
- if (!EFI_ERROR (SubCommandStatus) && (mPaginationCache != NULL)) {
- DumpParamsOut->HasMore = TRUE;
- }
- } else if (mPaginationCache != NULL) {
- DumpParamsOut->TotalSize = (UINT32)mPaginationCacheSize;
- DumpOutputBuffer = (UINT8 *)(DumpParamsOut + 1);
-
- // Make sure that we don't over-index the cache.
- DumpTotalPages = mPaginationCacheSize / VAR_CHECK_POLICY_MM_DUMP_BUFFER_SIZE;
- if (mPaginationCacheSize % VAR_CHECK_POLICY_MM_DUMP_BUFFER_SIZE != 0) {
- DumpTotalPages++;
- }
-
- if (DumpParamsIn->PageRequested > DumpTotalPages) {
- SubCommandStatus = EFI_INVALID_PARAMETER;
- } else {
- // Figure out how far into the page cache we need to go for our next page.
- // We know the blind subtraction won't be bad because we already checked for page 0.
- DumpInputBuffer = &mPaginationCache[VAR_CHECK_POLICY_MM_DUMP_BUFFER_SIZE * (DumpParamsIn->PageRequested - 1)];
- TempSize = VAR_CHECK_POLICY_MM_DUMP_BUFFER_SIZE;
- // If we're getting the last page, adjust the PageSize.
- if (DumpParamsIn->PageRequested == DumpTotalPages) {
- TempSize = mPaginationCacheSize % VAR_CHECK_POLICY_MM_DUMP_BUFFER_SIZE;
- }
-
- CopyMem (DumpOutputBuffer, DumpInputBuffer, TempSize);
- DumpParamsOut->PageSize = TempSize;
- // If we just got the last page, settle up the cache.
- if (DumpParamsIn->PageRequested == DumpTotalPages) {
- DumpParamsOut->HasMore = FALSE;
- FreePool (mPaginationCache);
- mPaginationCache = NULL;
- mPaginationCacheSize = 0;
- mCurrentPaginationCommand = 0;
- // Otherwise, we could do more here.
- } else {
- DumpParamsOut->HasMore = TRUE;
- }
-
- // If we made it this far, we're basically good.
- SubCommandStatus = EFI_SUCCESS;
- }
-
- // If we've requested any other page than 0 and the cache is empty, we must have timed out.
- } else {
- DumpParamsOut->TotalSize = 0;
- DumpParamsOut->PageSize = 0;
- DumpParamsOut->HasMore = FALSE;
- SubCommandStatus = EFI_TIMEOUT;
- }
-
- // There's currently no use for this, but it shouldn't be hard to implement.
- PolicyCommHeader->Result = SubCommandStatus;
- break;
-
- case VAR_CHECK_POLICY_COMMAND_LOCK:
- PolicyCommHeader->Result = LockVariablePolicy ();
- break;
-
- case VAR_CHECK_POLICY_COMMAND_GET_INFO:
- case VAR_CHECK_POLICY_COMMAND_GET_LOCK_VAR_STATE_INFO:
- ExpectedSize += VAR_CHECK_POLICY_COMM_GET_INFO_PARAMS_END + VAR_CHECK_POLICY_MM_GET_INFO_BUFFER_SIZE;
-
- if (InternalCommBufferSize < ExpectedSize) {
- PolicyCommHeader->Result = EFI_INVALID_PARAMETER;
- break;
- }
-
- GetInfoParamsInternal = (VAR_CHECK_POLICY_COMM_GET_INFO_PARAMS *)(InternalPolicyCommHeader + 1);
- GetInfoParamsExternal = (VAR_CHECK_POLICY_COMM_GET_INFO_PARAMS *)(PolicyCommHeader + 1);
-
- SubCommandStatus = SafeUintnSub (
- VAR_CHECK_POLICY_MM_GET_INFO_BUFFER_SIZE,
- GetInfoParamsInternal->InputVariableNameSize,
- &AllowedOutputVariableNameSize
- );
- if (EFI_ERROR (SubCommandStatus)) {
- PolicyCommHeader->Result = EFI_INVALID_PARAMETER;
- break;
- }
-
- if (GetInfoParamsInternal->OutputVariableNameSize > 0) {
- SubCommandStatus = SafeUintnAdd (
- ((UINTN)GetInfoParamsInternal + VAR_CHECK_POLICY_COMM_GET_INFO_PARAMS_END),
- (UINTN)GetInfoParamsInternal->InputVariableNameSize,
- (UINTN *)&InternalCopyOfOutputVariableName
- );
- if (EFI_ERROR (SubCommandStatus)) {
- PolicyCommHeader->Result = EFI_INVALID_PARAMETER;
- break;
- }
- } else {
- InternalCopyOfOutputVariableName = NULL;
- }
-
- ZeroMem (&GetInfoParamsInternal->OutputPolicyEntry, sizeof (GetInfoParamsInternal->OutputPolicyEntry));
- ZeroMem (&GetInfoParamsExternal->OutputPolicyEntry, sizeof (GetInfoParamsExternal->OutputPolicyEntry));
-
- LocalSize = (UINTN)GetInfoParamsInternal->OutputVariableNameSize;
-
- if (InternalPolicyCommHeader->Command == VAR_CHECK_POLICY_COMMAND_GET_INFO) {
- SubCommandStatus = GetVariablePolicyInfo (
- GetInfoParamsInternal->InputVariableName,
- &GetInfoParamsInternal->InputVendorGuid,
- &LocalSize,
- &GetInfoParamsInternal->OutputPolicyEntry.VariablePolicy,
- InternalCopyOfOutputVariableName
- );
- } else if (InternalPolicyCommHeader->Command == VAR_CHECK_POLICY_COMMAND_GET_LOCK_VAR_STATE_INFO) {
- SubCommandStatus = GetLockOnVariableStateVariablePolicyInfo (
- GetInfoParamsInternal->InputVariableName,
- &GetInfoParamsInternal->InputVendorGuid,
- &LocalSize,
- &GetInfoParamsInternal->OutputPolicyEntry.LockOnVarStatePolicy,
- InternalCopyOfOutputVariableName
- );
- } else {
- PolicyCommHeader->Result = EFI_INVALID_PARAMETER;
- break;
- }
-
- if (EFI_ERROR (SubCommandStatus) && (SubCommandStatus != EFI_BUFFER_TOO_SMALL)) {
- PolicyCommHeader->Result = SubCommandStatus;
- break;
- }
-
- if (EFI_ERROR (SafeUintnToUint32 (LocalSize, &GetInfoParamsInternal->OutputVariableNameSize))) {
- PolicyCommHeader->Result = EFI_BAD_BUFFER_SIZE;
- break;
- }
-
- ASSERT (sizeof (GetInfoParamsInternal->OutputPolicyEntry) == sizeof (GetInfoParamsExternal->OutputPolicyEntry));
- CopyMem (
- &GetInfoParamsExternal->OutputPolicyEntry,
- &GetInfoParamsInternal->OutputPolicyEntry,
- sizeof (GetInfoParamsExternal->OutputPolicyEntry)
- );
-
- GetInfoParamsExternal->OutputVariableNameSize = GetInfoParamsInternal->OutputVariableNameSize;
- if (SubCommandStatus == EFI_BUFFER_TOO_SMALL) {
- PolicyCommHeader->Result = EFI_BUFFER_TOO_SMALL;
- break;
- }
-
- SubCommandStatus = SafeUintnAdd (
- ((UINTN)GetInfoParamsExternal + VAR_CHECK_POLICY_COMM_GET_INFO_PARAMS_END),
- (UINTN)GetInfoParamsInternal->InputVariableNameSize,
- (UINTN *)&ExternalCopyOfOutputVariableName
- );
- if (EFI_ERROR (SubCommandStatus)) {
- PolicyCommHeader->Result = EFI_BAD_BUFFER_SIZE;
- break;
- }
-
- if (GetInfoParamsInternal->OutputVariableNameSize > 0) {
- SubCommandStatus = StrnCpyS (
- ExternalCopyOfOutputVariableName,
- AllowedOutputVariableNameSize,
- InternalCopyOfOutputVariableName,
- (UINTN)GetInfoParamsInternal->OutputVariableNameSize
- );
- ASSERT_EFI_ERROR (SubCommandStatus);
- } else {
- // The comm buffer should always have the space for the variable policy output
- // variable name. Fill it with NULL chars if a variable name is not present so
- // it has a consistent value in the case of variable name absence.
- SetMem (ExternalCopyOfOutputVariableName, AllowedOutputVariableNameSize, CHAR_NULL);
- }
-
- PolicyCommHeader->Result = SubCommandStatus;
-
- break;
-
- default:
- // Mark unknown requested command as EFI_UNSUPPORTED.
- DEBUG ((DEBUG_INFO, "%a - Invalid command requested! %d\n", __func__, PolicyCommHeader->Command));
- PolicyCommHeader->Result = EFI_UNSUPPORTED;
- break;
- }
-
- DEBUG ((
- DEBUG_VERBOSE,
- "%a - Command %d returning %r.\n",
- __func__,
- PolicyCommHeader->Command,
- PolicyCommHeader->Result
- ));
-
- return Status;
-}
-
-/**
- Constructor function of VarCheckPolicyLib to register VarCheck handler and
- SW MMI handlers.
-
- @retval EFI_SUCCESS The constructor executed correctly.
-
-**/
-EFI_STATUS
-EFIAPI
-VarCheckPolicyLibCommonConstructor (
- VOID
- )
-{
- EFI_STATUS Status;
- EFI_HANDLE DiscardedHandle;
-
- // Initialize the business logic with the internal GetVariable handler.
- Status = InitVariablePolicyLib (VariableServiceGetVariable);
-
- // Only proceed with init if the business logic could be initialized.
- if (!EFI_ERROR (Status)) {
- // Register the VarCheck handler for SetVariable filtering.
- // Forward the check to the business logic of the library.
- VarCheckLibRegisterSetVariableCheckHandler (ValidateSetVariable);
-
- // Register the MMI handlers for receiving policy commands.
- DiscardedHandle = NULL;
- Status = gMmst->MmiHandlerRegister (
- VarCheckPolicyLibMmiHandler,
- &gVarCheckPolicyLibMmiHandlerGuid,
- &DiscardedHandle
- );
- }
- // Otherwise, there's not much we can do.
- else {
- DEBUG ((DEBUG_ERROR, "%a - Cannot Initialize VariablePolicyLib! %r\n", __func__, Status));
- ASSERT_EFI_ERROR (Status);
- }
-
- return Status;
-}
+/** @file -- VarCheckPolicyLib.c
+This is a NULL library instance that leverages the VarCheck interface
+and the business logic behind the VariablePolicy code to make its decisions.
+
+Copyright (c) Microsoft Corporation.
+SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include <Library/VarCheckLib.h>
+#include <Library/BaseLib.h>
+#include <Library/DebugLib.h>
+#include <Library/SafeIntLib.h>
+#include <Library/MmServicesTableLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/MemoryAllocationLib.h>
+
+#include <Protocol/MmCommunication.h>
+
+#include <Protocol/VariablePolicy.h>
+#include <Library/VariablePolicyLib.h>
+
+#include <Guid/VarCheckPolicyMmi.h>
+
+#include "VarCheckPolicyLib.h"
+
+// ================================================
+// As a VarCheck library, we're linked into the VariableServices
+// and may not be able to call them indirectly. To get around this,
+// use the internal GetVariable function to query the variable store.
+// ================================================
+EFI_STATUS
+EFIAPI
+VariableServiceGetVariable (
+ IN CHAR16 *VariableName,
+ IN EFI_GUID *VendorGuid,
+ OUT UINT32 *Attributes OPTIONAL,
+ IN OUT UINTN *DataSize,
+ OUT VOID *Data
+ );
+
+UINT8 mSecurityEvalBuffer[VAR_CHECK_POLICY_MM_COMM_BUFFER_SIZE];
+
+// Pagination Cache Variables
+UINT8 *mPaginationCache = NULL;
+UINTN mPaginationCacheSize = 0;
+UINT32 mCurrentPaginationCommand = 0;
+
+/**
+ MM Communication Handler to recieve commands from the DXE protocol for
+ Variable Policies. This communication channel is used to register new policies
+ and poll and toggle the enforcement of variable policies.
+
+ @param[in] DispatchHandle All parameters standard to MM communications convention.
+ @param[in] RegisterContext All parameters standard to MM communications convention.
+ @param[in,out] CommBuffer All parameters standard to MM communications convention.
+ @param[in,out] CommBufferSize All parameters standard to MM communications convention.
+
+ @retval EFI_SUCCESS
+ @retval EFI_INVALID_PARAMETER CommBuffer or CommBufferSize is null pointer.
+ @retval EFI_INVALID_PARAMETER CommBuffer size is wrong.
+ @retval EFI_INVALID_PARAMETER Revision or signature don't match.
+
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+VarCheckPolicyLibMmiHandler (
+ IN EFI_HANDLE DispatchHandle,
+ IN CONST VOID *RegisterContext,
+ IN OUT VOID *CommBuffer,
+ IN OUT UINTN *CommBufferSize
+ )
+{
+ UINTN InternalCommBufferSize;
+ VOID *InternalCommBuffer;
+ EFI_STATUS Status;
+ EFI_STATUS SubCommandStatus;
+ VAR_CHECK_POLICY_COMM_HEADER *PolicyCommHeader;
+ VAR_CHECK_POLICY_COMM_HEADER *InternalPolicyCommHeader;
+ VAR_CHECK_POLICY_COMM_IS_ENABLED_PARAMS *IsEnabledParams;
+ VAR_CHECK_POLICY_COMM_DUMP_PARAMS *DumpParamsIn;
+ VAR_CHECK_POLICY_COMM_DUMP_PARAMS *DumpParamsOut;
+ VAR_CHECK_POLICY_COMM_GET_INFO_PARAMS *GetInfoParamsInternal;
+ VAR_CHECK_POLICY_COMM_GET_INFO_PARAMS *GetInfoParamsExternal;
+ CHAR16 *InternalCopyOfOutputVariableName;
+ CHAR16 *ExternalCopyOfOutputVariableName;
+ UINT8 *DumpInputBuffer;
+ UINT8 *DumpOutputBuffer;
+ UINTN AllowedOutputVariableNameSize;
+ UINTN DumpTotalPages;
+ UINTN LocalSize;
+ VARIABLE_POLICY_ENTRY *PolicyEntry;
+ UINTN ExpectedSize;
+ UINT32 TempSize;
+
+ Status = EFI_SUCCESS;
+
+ //
+ // Validate some input parameters.
+ //
+ // If either of the pointers are NULL, we can't proceed.
+ if ((CommBuffer == NULL) || (CommBufferSize == NULL)) {
+ DEBUG ((DEBUG_INFO, "%a - Invalid comm buffer pointers!\n", __func__));
+ return EFI_INVALID_PARAMETER;
+ }
+
+ // Make sure that the buffer does not overlap SMM.
+ // This should be covered by the SmiManage infrastructure, but just to be safe...
+ InternalCommBufferSize = *CommBufferSize;
+ if ((InternalCommBufferSize > VAR_CHECK_POLICY_MM_COMM_BUFFER_SIZE) ||
+ !VarCheckPolicyIsBufferOutsideValid ((UINTN)CommBuffer, (UINT64)InternalCommBufferSize))
+ {
+ DEBUG ((DEBUG_ERROR, "%a - Invalid CommBuffer supplied! 0x%016lX[0x%016lX]\n", __func__, CommBuffer, InternalCommBufferSize));
+ return EFI_INVALID_PARAMETER;
+ }
+
+ // If the size does not meet a minimum threshold, we cannot proceed.
+ ExpectedSize = sizeof (VAR_CHECK_POLICY_COMM_HEADER);
+ if (InternalCommBufferSize < ExpectedSize) {
+ DEBUG ((DEBUG_INFO, "%a - Bad comm buffer size! %d < %d\n", __func__, InternalCommBufferSize, ExpectedSize));
+ return EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // Before proceeding any further, copy the buffer internally so that we can compare
+ // without worrying about TOCTOU.
+ //
+ InternalCommBuffer = &mSecurityEvalBuffer[0];
+ CopyMem (InternalCommBuffer, CommBuffer, InternalCommBufferSize);
+ PolicyCommHeader = CommBuffer;
+ InternalPolicyCommHeader = InternalCommBuffer;
+ // Check the revision and the signature of the comm header.
+ if ((InternalPolicyCommHeader->Signature != VAR_CHECK_POLICY_COMM_SIG) ||
+ (InternalPolicyCommHeader->Revision != VAR_CHECK_POLICY_COMM_REVISION))
+ {
+ DEBUG ((DEBUG_INFO, "%a - Signature or revision are incorrect!\n", __func__));
+ // We have verified the buffer is not null and have enough size to hold Result field.
+ PolicyCommHeader->Result = EFI_INVALID_PARAMETER;
+ return EFI_SUCCESS;
+ }
+
+ // If we're in the middle of a paginated dump and any other command is sent,
+ // pagination cache must be cleared.
+ if ((mPaginationCache != NULL) && (InternalPolicyCommHeader->Command != mCurrentPaginationCommand)) {
+ FreePool (mPaginationCache);
+ mPaginationCache = NULL;
+ mPaginationCacheSize = 0;
+ mCurrentPaginationCommand = 0;
+ }
+
+ //
+ // Now we can process the command as it was sent.
+ //
+ PolicyCommHeader->Result = EFI_ABORTED; // Set a default return for incomplete commands.
+ switch (InternalPolicyCommHeader->Command) {
+ case VAR_CHECK_POLICY_COMMAND_DISABLE:
+ PolicyCommHeader->Result = DisableVariablePolicy ();
+ break;
+
+ case VAR_CHECK_POLICY_COMMAND_IS_ENABLED:
+ // Make sure that we're dealing with a reasonable size.
+ // This add should be safe because these are fixed sizes so far.
+ ExpectedSize += sizeof (VAR_CHECK_POLICY_COMM_IS_ENABLED_PARAMS);
+ if (InternalCommBufferSize < ExpectedSize) {
+ DEBUG ((DEBUG_INFO, "%a - Bad comm buffer size! %d < %d\n", __func__, InternalCommBufferSize, ExpectedSize));
+ PolicyCommHeader->Result = EFI_INVALID_PARAMETER;
+ break;
+ }
+
+ // Now that we know we've got a valid size, we can fill in the rest of the data.
+ IsEnabledParams = (VAR_CHECK_POLICY_COMM_IS_ENABLED_PARAMS *)((UINT8 *)CommBuffer + sizeof (VAR_CHECK_POLICY_COMM_HEADER));
+ IsEnabledParams->State = IsVariablePolicyEnabled ();
+ PolicyCommHeader->Result = EFI_SUCCESS;
+ break;
+
+ case VAR_CHECK_POLICY_COMMAND_REGISTER:
+ // Make sure that we're dealing with a reasonable size.
+ // This add should be safe because these are fixed sizes so far.
+ ExpectedSize += sizeof (VARIABLE_POLICY_ENTRY);
+ if (InternalCommBufferSize < ExpectedSize) {
+ DEBUG ((DEBUG_INFO, "%a - Bad comm buffer size! %d < %d\n", __func__, InternalCommBufferSize, ExpectedSize));
+ PolicyCommHeader->Result = EFI_INVALID_PARAMETER;
+ break;
+ }
+
+ // At the very least, we can assume that we're working with a valid policy entry.
+ // Time to compare its internal size.
+ PolicyEntry = (VARIABLE_POLICY_ENTRY *)((UINT8 *)InternalCommBuffer + sizeof (VAR_CHECK_POLICY_COMM_HEADER));
+ if ((PolicyEntry->Version != VARIABLE_POLICY_ENTRY_REVISION) ||
+ (PolicyEntry->Size < sizeof (VARIABLE_POLICY_ENTRY)) ||
+ EFI_ERROR (SafeUintnAdd (sizeof (VAR_CHECK_POLICY_COMM_HEADER), PolicyEntry->Size, &ExpectedSize)) ||
+ (InternalCommBufferSize < ExpectedSize))
+ {
+ DEBUG ((DEBUG_INFO, "%a - Bad policy entry contents!\n", __func__));
+ PolicyCommHeader->Result = EFI_INVALID_PARAMETER;
+ break;
+ }
+
+ PolicyCommHeader->Result = RegisterVariablePolicy (PolicyEntry);
+ break;
+
+ case VAR_CHECK_POLICY_COMMAND_DUMP:
+ // Make sure that we're dealing with a reasonable size.
+ // This add should be safe because these are fixed sizes so far.
+ ExpectedSize += sizeof (VAR_CHECK_POLICY_COMM_DUMP_PARAMS) + VAR_CHECK_POLICY_MM_DUMP_BUFFER_SIZE;
+ if (InternalCommBufferSize < ExpectedSize) {
+ DEBUG ((DEBUG_INFO, "%a - Bad comm buffer size! %d < %d\n", __func__, InternalCommBufferSize, ExpectedSize));
+ PolicyCommHeader->Result = EFI_INVALID_PARAMETER;
+ break;
+ }
+
+ // Now that we know we've got a valid size, we can fill in the rest of the data.
+ DumpParamsIn = (VAR_CHECK_POLICY_COMM_DUMP_PARAMS *)(InternalPolicyCommHeader + 1);
+ DumpParamsOut = (VAR_CHECK_POLICY_COMM_DUMP_PARAMS *)(PolicyCommHeader + 1);
+
+ // If we're requesting the first page, initialize the cache and get the sizes.
+ if (DumpParamsIn->PageRequested == 0) {
+ if (mPaginationCache != NULL) {
+ FreePool (mPaginationCache);
+ mPaginationCache = NULL;
+ }
+
+ // Determine what the required size is going to be.
+ DumpParamsOut->TotalSize = 0;
+ DumpParamsOut->PageSize = 0;
+ DumpParamsOut->HasMore = FALSE;
+ TempSize = 0;
+ SubCommandStatus = DumpVariablePolicy (NULL, &TempSize);
+ if ((SubCommandStatus == EFI_BUFFER_TOO_SMALL) && (TempSize > 0)) {
+ mCurrentPaginationCommand = VAR_CHECK_POLICY_COMMAND_DUMP;
+ mPaginationCacheSize = TempSize;
+ DumpParamsOut->TotalSize = TempSize;
+ mPaginationCache = AllocatePool (mPaginationCacheSize);
+ if (mPaginationCache == NULL) {
+ SubCommandStatus = EFI_OUT_OF_RESOURCES;
+ }
+ }
+
+ // If we've allocated our pagination cache, we're good to cache.
+ if (mPaginationCache != NULL) {
+ SubCommandStatus = DumpVariablePolicy (mPaginationCache, &TempSize);
+ }
+
+ // Populate the remaining fields and we can boogie.
+ if (!EFI_ERROR (SubCommandStatus) && (mPaginationCache != NULL)) {
+ DumpParamsOut->HasMore = TRUE;
+ }
+ } else if (mPaginationCache != NULL) {
+ DumpParamsOut->TotalSize = (UINT32)mPaginationCacheSize;
+ DumpOutputBuffer = (UINT8 *)(DumpParamsOut + 1);
+
+ // Make sure that we don't over-index the cache.
+ DumpTotalPages = mPaginationCacheSize / VAR_CHECK_POLICY_MM_DUMP_BUFFER_SIZE;
+ if (mPaginationCacheSize % VAR_CHECK_POLICY_MM_DUMP_BUFFER_SIZE != 0) {
+ DumpTotalPages++;
+ }
+
+ if (DumpParamsIn->PageRequested > DumpTotalPages) {
+ SubCommandStatus = EFI_INVALID_PARAMETER;
+ } else {
+ // Figure out how far into the page cache we need to go for our next page.
+ // We know the blind subtraction won't be bad because we already checked for page 0.
+ DumpInputBuffer = &mPaginationCache[VAR_CHECK_POLICY_MM_DUMP_BUFFER_SIZE * (DumpParamsIn->PageRequested - 1)];
+ TempSize = VAR_CHECK_POLICY_MM_DUMP_BUFFER_SIZE;
+ // If we're getting the last page, adjust the PageSize.
+ if (DumpParamsIn->PageRequested == DumpTotalPages) {
+ TempSize = mPaginationCacheSize % VAR_CHECK_POLICY_MM_DUMP_BUFFER_SIZE;
+ }
+
+ CopyMem (DumpOutputBuffer, DumpInputBuffer, TempSize);
+ DumpParamsOut->PageSize = TempSize;
+ // If we just got the last page, settle up the cache.
+ if (DumpParamsIn->PageRequested == DumpTotalPages) {
+ DumpParamsOut->HasMore = FALSE;
+ FreePool (mPaginationCache);
+ mPaginationCache = NULL;
+ mPaginationCacheSize = 0;
+ mCurrentPaginationCommand = 0;
+ // Otherwise, we could do more here.
+ } else {
+ DumpParamsOut->HasMore = TRUE;
+ }
+
+ // If we made it this far, we're basically good.
+ SubCommandStatus = EFI_SUCCESS;
+ }
+
+ // If we've requested any other page than 0 and the cache is empty, we must have timed out.
+ } else {
+ DumpParamsOut->TotalSize = 0;
+ DumpParamsOut->PageSize = 0;
+ DumpParamsOut->HasMore = FALSE;
+ SubCommandStatus = EFI_TIMEOUT;
+ }
+
+ // There's currently no use for this, but it shouldn't be hard to implement.
+ PolicyCommHeader->Result = SubCommandStatus;
+ break;
+
+ case VAR_CHECK_POLICY_COMMAND_LOCK:
+ PolicyCommHeader->Result = LockVariablePolicy ();
+ break;
+
+ case VAR_CHECK_POLICY_COMMAND_GET_INFO:
+ case VAR_CHECK_POLICY_COMMAND_GET_LOCK_VAR_STATE_INFO:
+ ExpectedSize += VAR_CHECK_POLICY_COMM_GET_INFO_PARAMS_END + VAR_CHECK_POLICY_MM_GET_INFO_BUFFER_SIZE;
+
+ if (InternalCommBufferSize < ExpectedSize) {
+ PolicyCommHeader->Result = EFI_INVALID_PARAMETER;
+ break;
+ }
+
+ GetInfoParamsInternal = (VAR_CHECK_POLICY_COMM_GET_INFO_PARAMS *)(InternalPolicyCommHeader + 1);
+ GetInfoParamsExternal = (VAR_CHECK_POLICY_COMM_GET_INFO_PARAMS *)(PolicyCommHeader + 1);
+
+ SubCommandStatus = SafeUintnSub (
+ VAR_CHECK_POLICY_MM_GET_INFO_BUFFER_SIZE,
+ GetInfoParamsInternal->InputVariableNameSize,
+ &AllowedOutputVariableNameSize
+ );
+ if (EFI_ERROR (SubCommandStatus)) {
+ PolicyCommHeader->Result = EFI_INVALID_PARAMETER;
+ break;
+ }
+
+ if (GetInfoParamsInternal->OutputVariableNameSize > 0) {
+ SubCommandStatus = SafeUintnAdd (
+ ((UINTN)GetInfoParamsInternal + VAR_CHECK_POLICY_COMM_GET_INFO_PARAMS_END),
+ (UINTN)GetInfoParamsInternal->InputVariableNameSize,
+ (UINTN *)&InternalCopyOfOutputVariableName
+ );
+ if (EFI_ERROR (SubCommandStatus)) {
+ PolicyCommHeader->Result = EFI_INVALID_PARAMETER;
+ break;
+ }
+ } else {
+ InternalCopyOfOutputVariableName = NULL;
+ }
+
+ ZeroMem (&GetInfoParamsInternal->OutputPolicyEntry, sizeof (GetInfoParamsInternal->OutputPolicyEntry));
+ ZeroMem (&GetInfoParamsExternal->OutputPolicyEntry, sizeof (GetInfoParamsExternal->OutputPolicyEntry));
+
+ LocalSize = (UINTN)GetInfoParamsInternal->OutputVariableNameSize;
+
+ if (InternalPolicyCommHeader->Command == VAR_CHECK_POLICY_COMMAND_GET_INFO) {
+ SubCommandStatus = GetVariablePolicyInfo (
+ GetInfoParamsInternal->InputVariableName,
+ &GetInfoParamsInternal->InputVendorGuid,
+ &LocalSize,
+ &GetInfoParamsInternal->OutputPolicyEntry.VariablePolicy,
+ InternalCopyOfOutputVariableName
+ );
+ } else if (InternalPolicyCommHeader->Command == VAR_CHECK_POLICY_COMMAND_GET_LOCK_VAR_STATE_INFO) {
+ SubCommandStatus = GetLockOnVariableStateVariablePolicyInfo (
+ GetInfoParamsInternal->InputVariableName,
+ &GetInfoParamsInternal->InputVendorGuid,
+ &LocalSize,
+ &GetInfoParamsInternal->OutputPolicyEntry.LockOnVarStatePolicy,
+ InternalCopyOfOutputVariableName
+ );
+ } else {
+ PolicyCommHeader->Result = EFI_INVALID_PARAMETER;
+ break;
+ }
+
+ if (EFI_ERROR (SubCommandStatus) && (SubCommandStatus != EFI_BUFFER_TOO_SMALL)) {
+ PolicyCommHeader->Result = SubCommandStatus;
+ break;
+ }
+
+ if (EFI_ERROR (SafeUintnToUint32 (LocalSize, &GetInfoParamsInternal->OutputVariableNameSize))) {
+ PolicyCommHeader->Result = EFI_BAD_BUFFER_SIZE;
+ break;
+ }
+
+ ASSERT (sizeof (GetInfoParamsInternal->OutputPolicyEntry) == sizeof (GetInfoParamsExternal->OutputPolicyEntry));
+ CopyMem (
+ &GetInfoParamsExternal->OutputPolicyEntry,
+ &GetInfoParamsInternal->OutputPolicyEntry,
+ sizeof (GetInfoParamsExternal->OutputPolicyEntry)
+ );
+
+ GetInfoParamsExternal->OutputVariableNameSize = GetInfoParamsInternal->OutputVariableNameSize;
+ if (SubCommandStatus == EFI_BUFFER_TOO_SMALL) {
+ PolicyCommHeader->Result = EFI_BUFFER_TOO_SMALL;
+ break;
+ }
+
+ SubCommandStatus = SafeUintnAdd (
+ ((UINTN)GetInfoParamsExternal + VAR_CHECK_POLICY_COMM_GET_INFO_PARAMS_END),
+ (UINTN)GetInfoParamsInternal->InputVariableNameSize,
+ (UINTN *)&ExternalCopyOfOutputVariableName
+ );
+ if (EFI_ERROR (SubCommandStatus)) {
+ PolicyCommHeader->Result = EFI_BAD_BUFFER_SIZE;
+ break;
+ }
+
+ if (GetInfoParamsInternal->OutputVariableNameSize > 0) {
+ SubCommandStatus = StrnCpyS (
+ ExternalCopyOfOutputVariableName,
+ AllowedOutputVariableNameSize,
+ InternalCopyOfOutputVariableName,
+ (UINTN)GetInfoParamsInternal->OutputVariableNameSize
+ );
+ ASSERT_EFI_ERROR (SubCommandStatus);
+ } else {
+ // The comm buffer should always have the space for the variable policy output
+ // variable name. Fill it with NULL chars if a variable name is not present so
+ // it has a consistent value in the case of variable name absence.
+ SetMem (ExternalCopyOfOutputVariableName, AllowedOutputVariableNameSize, CHAR_NULL);
+ }
+
+ PolicyCommHeader->Result = SubCommandStatus;
+
+ break;
+
+ default:
+ // Mark unknown requested command as EFI_UNSUPPORTED.
+ DEBUG ((DEBUG_INFO, "%a - Invalid command requested! %d\n", __func__, PolicyCommHeader->Command));
+ PolicyCommHeader->Result = EFI_UNSUPPORTED;
+ break;
+ }
+
+ DEBUG ((
+ DEBUG_VERBOSE,
+ "%a - Command %d returning %r.\n",
+ __func__,
+ PolicyCommHeader->Command,
+ PolicyCommHeader->Result
+ ));
+
+ return Status;
+}
+
+/**
+ Constructor function of VarCheckPolicyLib to register VarCheck handler and
+ SW MMI handlers.
+
+ @retval EFI_SUCCESS The constructor executed correctly.
+
+**/
+EFI_STATUS
+EFIAPI
+VarCheckPolicyLibCommonConstructor (
+ VOID
+ )
+{
+ EFI_STATUS Status;
+ EFI_HANDLE DiscardedHandle;
+
+ // Initialize the business logic with the internal GetVariable handler.
+ Status = InitVariablePolicyLib (VariableServiceGetVariable);
+
+ // Only proceed with init if the business logic could be initialized.
+ if (!EFI_ERROR (Status)) {
+ // Register the VarCheck handler for SetVariable filtering.
+ // Forward the check to the business logic of the library.
+ VarCheckLibRegisterSetVariableCheckHandler (ValidateSetVariable);
+
+ // Register the MMI handlers for receiving policy commands.
+ DiscardedHandle = NULL;
+ Status = gMmst->MmiHandlerRegister (
+ VarCheckPolicyLibMmiHandler,
+ &gVarCheckPolicyLibMmiHandlerGuid,
+ &DiscardedHandle
+ );
+ }
+ // Otherwise, there's not much we can do.
+ else {
+ DEBUG ((DEBUG_ERROR, "%a - Cannot Initialize VariablePolicyLib! %r\n", __func__, Status));
+ ASSERT_EFI_ERROR (Status);
+ }
+
+ return Status;
+}