MdeModulePkg: Define the VarCheckPolicyLib and SMM interface

https://bugzilla.tianocore.org/show_bug.cgi?id=2522

VariablePolicy is an updated interface to
replace VarLock and VarCheckProtocol.

This is an instance of a VarCheckLib that is backed by the
VariablePolicyLib business logic. It also publishes the SMM
calling interface for messages from the DXE protocol.

Cc: Jian J Wang <jian.j.wang@intel.com>
Cc: Hao A Wu <hao.a.wu@intel.com>
Cc: Liming Gao <liming.gao@intel.com>
Cc: Bret Barkelew <brbarkel@microsoft.com>
Signed-off-by: Bret Barkelew <brbarkel@microsoft.com>
Reviewed-by: Dandan Bi <dandan.bi@intel.com>
Acked-by: Jian J Wang <jian.j.wang@intel.com>
This commit is contained in:
Bret Barkelew 2020-11-09 14:45:13 +08:00 committed by mergify[bot]
parent b6104becb9
commit 483449c9da
6 changed files with 460 additions and 0 deletions

View File

@ -0,0 +1,54 @@
/** @file -- VarCheckPolicyMmiCommon.h
This header contains communication definitions that are shared between DXE
and the MM component of VarCheckPolicy.
Copyright (c) Microsoft Corporation.
SPDX-License-Identifier: BSD-2-Clause-Patent
**/
#ifndef _VAR_CHECK_POLICY_MMI_COMMON_H_
#define _VAR_CHECK_POLICY_MMI_COMMON_H_
#define VAR_CHECK_POLICY_COMM_SIG SIGNATURE_32('V', 'C', 'P', 'C')
#define VAR_CHECK_POLICY_COMM_REVISION 1
#pragma pack(push, 1)
typedef struct _VAR_CHECK_POLICY_COMM_HEADER {
UINT32 Signature;
UINT32 Revision;
UINT32 Command;
EFI_STATUS Result;
} VAR_CHECK_POLICY_COMM_HEADER;
typedef struct _VAR_CHECK_POLICY_COMM_IS_ENABLED_PARAMS {
BOOLEAN State;
} VAR_CHECK_POLICY_COMM_IS_ENABLED_PARAMS;
typedef struct _VAR_CHECK_POLICY_COMM_DUMP_PARAMS {
UINT32 PageRequested;
UINT32 TotalSize;
UINT32 PageSize;
BOOLEAN HasMore;
} VAR_CHECK_POLICY_COMM_DUMP_PARAMS;
#pragma pack(pop)
// Make sure that we will hold at least the headers.
#define VAR_CHECK_POLICY_MM_COMM_BUFFER_SIZE MAX((OFFSET_OF(EFI_MM_COMMUNICATE_HEADER, Data) + sizeof (VAR_CHECK_POLICY_COMM_HEADER) + EFI_PAGES_TO_SIZE(1)), EFI_PAGES_TO_SIZE(4))
#define VAR_CHECK_POLICY_MM_DUMP_BUFFER_SIZE (VAR_CHECK_POLICY_MM_COMM_BUFFER_SIZE - \
(OFFSET_OF(EFI_MM_COMMUNICATE_HEADER, Data) + \
sizeof(VAR_CHECK_POLICY_COMM_HEADER) + \
sizeof(VAR_CHECK_POLICY_COMM_DUMP_PARAMS)))
STATIC_ASSERT (
VAR_CHECK_POLICY_MM_DUMP_BUFFER_SIZE < VAR_CHECK_POLICY_MM_COMM_BUFFER_SIZE,
"an integer underflow may have occurred calculating VAR_CHECK_POLICY_MM_DUMP_BUFFER_SIZE"
);
#define VAR_CHECK_POLICY_COMMAND_DISABLE 0x0001
#define VAR_CHECK_POLICY_COMMAND_IS_ENABLED 0x0002
#define VAR_CHECK_POLICY_COMMAND_REGISTER 0x0003
#define VAR_CHECK_POLICY_COMMAND_DUMP 0x0004
#define VAR_CHECK_POLICY_COMMAND_LOCK 0x0005
#endif // _VAR_CHECK_POLICY_MMI_COMMON_H_

View File

@ -0,0 +1,346 @@
/** @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/SmmMemLib.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>
//================================================
// 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 *PolicyCommmHeader;
VAR_CHECK_POLICY_COMM_HEADER *InternalPolicyCommmHeader;
VAR_CHECK_POLICY_COMM_IS_ENABLED_PARAMS *IsEnabledParams;
VAR_CHECK_POLICY_COMM_DUMP_PARAMS *DumpParamsIn;
VAR_CHECK_POLICY_COMM_DUMP_PARAMS *DumpParamsOut;
UINT8 *DumpInputBuffer;
UINT8 *DumpOutputBuffer;
UINTN DumpTotalPages;
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", __FUNCTION__ ));
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 || !SmmIsBufferOutsideSmmValid((UINTN)CommBuffer, (UINT64)InternalCommBufferSize)) {
DEBUG ((DEBUG_ERROR, "%a - Invalid CommBuffer supplied! 0x%016lX[0x%016lX]\n", __FUNCTION__, 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", __FUNCTION__, 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);
PolicyCommmHeader = CommBuffer;
InternalPolicyCommmHeader = InternalCommBuffer;
// Check the revision and the signature of the comm header.
if (InternalPolicyCommmHeader->Signature != VAR_CHECK_POLICY_COMM_SIG ||
InternalPolicyCommmHeader->Revision != VAR_CHECK_POLICY_COMM_REVISION) {
DEBUG(( DEBUG_INFO, "%a - Signature or revision are incorrect!\n", __FUNCTION__ ));
// We have verified the buffer is not null and have enough size to hold Result field.
PolicyCommmHeader->Result = EFI_INVALID_PARAMETER;
return EFI_SUCCESS;
}
// If we're in the middle of a paginated dump and any other command is sent,
// pagination cache must be cleared.
if (mPaginationCache != NULL && InternalPolicyCommmHeader->Command != mCurrentPaginationCommand) {
FreePool (mPaginationCache);
mPaginationCache = NULL;
mPaginationCacheSize = 0;
mCurrentPaginationCommand = 0;
}
//
// Now we can process the command as it was sent.
//
PolicyCommmHeader->Result = EFI_ABORTED; // Set a default return for incomplete commands.
switch(InternalPolicyCommmHeader->Command) {
case VAR_CHECK_POLICY_COMMAND_DISABLE:
PolicyCommmHeader->Result = DisableVariablePolicy();
break;
case VAR_CHECK_POLICY_COMMAND_IS_ENABLED:
// Make sure that we're dealing with a reasonable size.
// This add should be safe because these are fixed sizes so far.
ExpectedSize += sizeof(VAR_CHECK_POLICY_COMM_IS_ENABLED_PARAMS);
if (InternalCommBufferSize < ExpectedSize) {
DEBUG(( DEBUG_INFO, "%a - Bad comm buffer size! %d < %d\n", __FUNCTION__, InternalCommBufferSize, ExpectedSize ));
PolicyCommmHeader->Result = EFI_INVALID_PARAMETER;
break;
}
// Now that we know we've got a valid size, we can fill in the rest of the data.
IsEnabledParams = (VAR_CHECK_POLICY_COMM_IS_ENABLED_PARAMS*)((UINT8*)CommBuffer + sizeof(VAR_CHECK_POLICY_COMM_HEADER));
IsEnabledParams->State = IsVariablePolicyEnabled();
PolicyCommmHeader->Result = EFI_SUCCESS;
break;
case VAR_CHECK_POLICY_COMMAND_REGISTER:
// Make sure that we're dealing with a reasonable size.
// This add should be safe because these are fixed sizes so far.
ExpectedSize += sizeof(VARIABLE_POLICY_ENTRY);
if (InternalCommBufferSize < ExpectedSize) {
DEBUG(( DEBUG_INFO, "%a - Bad comm buffer size! %d < %d\n", __FUNCTION__, InternalCommBufferSize, ExpectedSize ));
PolicyCommmHeader->Result = EFI_INVALID_PARAMETER;
break;
}
// At the very least, we can assume that we're working with a valid policy entry.
// Time to compare its internal size.
PolicyEntry = (VARIABLE_POLICY_ENTRY*)((UINT8*)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", __FUNCTION__ ));
PolicyCommmHeader->Result = EFI_INVALID_PARAMETER;
break;
}
PolicyCommmHeader->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", __FUNCTION__, InternalCommBufferSize, ExpectedSize ));
PolicyCommmHeader->Result = EFI_INVALID_PARAMETER;
break;
}
// Now that we know we've got a valid size, we can fill in the rest of the data.
DumpParamsIn = (VAR_CHECK_POLICY_COMM_DUMP_PARAMS*)(InternalPolicyCommmHeader + 1);
DumpParamsOut = (VAR_CHECK_POLICY_COMM_DUMP_PARAMS*)(PolicyCommmHeader + 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;
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.
PolicyCommmHeader->Result = SubCommandStatus;
break;
case VAR_CHECK_POLICY_COMMAND_LOCK:
PolicyCommmHeader->Result = LockVariablePolicy();
break;
default:
// Mark unknown requested command as EFI_UNSUPPORTED.
DEBUG(( DEBUG_INFO, "%a - Invalid command requested! %d\n", __FUNCTION__, PolicyCommmHeader->Command ));
PolicyCommmHeader->Result = EFI_UNSUPPORTED;
break;
}
DEBUG(( DEBUG_VERBOSE, "%a - Command %d returning %r.\n", __FUNCTION__,
PolicyCommmHeader->Command, PolicyCommmHeader->Result ));
return Status;
}
/**
Constructor function of VarCheckPolicyLib to register VarCheck handler and
SW MMI handlers.
@param[in] ImageHandle The firmware allocated handle for the EFI image.
@param[in] SystemTable A pointer to the EFI System Table.
@retval EFI_SUCCESS The constructor executed correctly.
**/
EFI_STATUS
EFIAPI
VarCheckPolicyLibConstructor (
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
{
EFI_STATUS Status;
EFI_HANDLE DiscardedHandle;
// Initialize the business logic with the internal GetVariable handler.
Status = InitVariablePolicyLib( VariableServiceGetVariable );
// Only proceed with init if the business logic could be initialized.
if (!EFI_ERROR( Status )) {
// Register the VarCheck handler for SetVariable filtering.
// Forward the check to the business logic of the library.
VarCheckLibRegisterSetVariableCheckHandler( ValidateSetVariable );
// Register the MMI handlers for receiving policy commands.
DiscardedHandle = NULL;
Status = gMmst->MmiHandlerRegister( VarCheckPolicyLibMmiHandler,
&gVarCheckPolicyLibMmiHandlerGuid,
&DiscardedHandle );
}
// Otherwise, there's not much we can do.
else {
DEBUG(( DEBUG_ERROR, "%a - Cannot Initialize VariablePolicyLib! %r\n", __FUNCTION__, Status ));
ASSERT_EFI_ERROR( Status );
}
return Status;
}

View File

@ -0,0 +1,42 @@
## @file VarCheckPolicyLib.inf
# This is an instance of a VarCheck lib that leverages the business logic behind
# the VariablePolicy code to make its decisions.
#
# Copyright (c) Microsoft Corporation.
# SPDX-License-Identifier: BSD-2-Clause-Patent
##
[Defines]
INF_VERSION = 0x00010005
BASE_NAME = VarCheckPolicyLib
FILE_GUID = 9C28A48F-C884-4B1F-8B95-DEF125448023
MODULE_TYPE = DXE_RUNTIME_DRIVER
VERSION_STRING = 1.0
LIBRARY_CLASS = NULL|DXE_RUNTIME_DRIVER DXE_SMM_DRIVER
CONSTRUCTOR = VarCheckPolicyLibConstructor
[Sources]
VarCheckPolicyLib.c
[Packages]
MdePkg/MdePkg.dec
MdeModulePkg/MdeModulePkg.dec
[LibraryClasses]
BaseLib
DebugLib
BaseMemoryLib
DxeServicesLib
MemoryAllocationLib
VarCheckLib
VariablePolicyLib
VariablePolicyHelperLib
SafeIntLib
MmServicesTableLib
[Guids]
gVarCheckPolicyLibMmiHandlerGuid ## CONSUME ## Used to register for MM Communication events.

View File

@ -0,0 +1,12 @@
// /** @file
// VarCheckPolicyLib.uni
//
// Copyright (c) Microsoft Corporation.
// SPDX-License-Identifier: BSD-2-Clause-Patent
//
// **/
#string STR_MODULE_ABSTRACT #language en-US "NULL library implementation that conforms to the VarCheck interface to allow VariablePolicy engine to enforce policies"
#string STR_MODULE_DESCRIPTION #language en-US "NULL library implementation that conforms to the VarCheck interface to allow VariablePolicy engine to enforce policies"

View File

@ -385,6 +385,10 @@
## Include/Guid/EndofS3Resume.h
gEdkiiEndOfS3ResumeGuid = { 0x96f5296d, 0x05f7, 0x4f3c, {0x84, 0x67, 0xe4, 0x56, 0x89, 0x0e, 0x0c, 0xb5 } }
## Used (similar to Variable Services) to communicate policies to the enforcement engine.
# {DA1B0D11-D1A7-46C4-9DC9-F3714875C6EB}
gVarCheckPolicyLibMmiHandlerGuid = { 0xda1b0d11, 0xd1a7, 0x46c4, { 0x9d, 0xc9, 0xf3, 0x71, 0x48, 0x75, 0xc6, 0xeb }}
## Include/Guid/S3SmmInitDone.h
gEdkiiS3SmmInitDoneGuid = { 0x8f9d4825, 0x797d, 0x48fc, { 0x84, 0x71, 0x84, 0x50, 0x25, 0x79, 0x2e, 0xf6 } }

View File

@ -313,6 +313,7 @@
MdeModulePkg/Library/AuthVariableLibNull/AuthVariableLibNull.inf
MdeModulePkg/Library/VariablePolicyLib/VariablePolicyLib.inf
MdeModulePkg/Library/VariablePolicyLib/VariablePolicyLibRuntimeDxe.inf
MdeModulePkg/Library/VarCheckPolicyLib/VarCheckPolicyLib.inf
MdeModulePkg/Library/VarCheckLib/VarCheckLib.inf
MdeModulePkg/Library/VarCheckHiiLib/VarCheckHiiLib.inf
MdeModulePkg/Library/VarCheckPcdLib/VarCheckPcdLib.inf
@ -458,6 +459,7 @@
MdeModulePkg/Core/PiSmmCore/PiSmmCore.inf
MdeModulePkg/Universal/Variable/RuntimeDxe/VariableSmm.inf {
<LibraryClasses>
NULL|MdeModulePkg/Library/VarCheckPolicyLib/VarCheckPolicyLib.inf
NULL|MdeModulePkg/Library/VarCheckUefiLib/VarCheckUefiLib.inf
NULL|MdeModulePkg/Library/VarCheckHiiLib/VarCheckHiiLib.inf
NULL|MdeModulePkg/Library/VarCheckPcdLib/VarCheckPcdLib.inf