/** @file | |
LockBox SMM driver. | |
Caution: This module requires additional review when modified. | |
This driver will have external input - communicate buffer in SMM mode. | |
This external input must be validated carefully to avoid security issue like | |
buffer overflow, integer overflow. | |
SmmLockBoxHandler(), SmmLockBoxRestore(), SmmLockBoxUpdate(), SmmLockBoxSave() | |
will receive untrusted input and do basic validation. | |
Copyright (c) 2010 - 2018, Intel Corporation. All rights reserved.<BR> | |
SPDX-License-Identifier: BSD-2-Clause-Patent | |
**/ | |
#include <PiSmm.h> | |
#include <Library/UefiDriverEntryPoint.h> | |
#include <Library/UefiBootServicesTableLib.h> | |
#include <Library/UefiRuntimeServicesTableLib.h> | |
#include <Library/SmmServicesTableLib.h> | |
#include <Library/BaseLib.h> | |
#include <Library/BaseMemoryLib.h> | |
#include <Library/DebugLib.h> | |
#include <Library/SmmMemLib.h> | |
#include <Library/LockBoxLib.h> | |
#include <Protocol/SmmReadyToLock.h> | |
#include <Protocol/SmmCommunication.h> | |
#include <Protocol/LockBox.h> | |
#include <Guid/SmmLockBox.h> | |
BOOLEAN mLocked = FALSE; | |
/** | |
Dispatch function for SMM lock box save. | |
Caution: This function may receive untrusted input. | |
Restore buffer and length are external input, so this function will validate | |
it is in SMRAM. | |
@param LockBoxParameterSave parameter of lock box save | |
**/ | |
VOID | |
SmmLockBoxSave ( | |
IN EFI_SMM_LOCK_BOX_PARAMETER_SAVE *LockBoxParameterSave | |
) | |
{ | |
EFI_STATUS Status; | |
EFI_SMM_LOCK_BOX_PARAMETER_SAVE TempLockBoxParameterSave; | |
// | |
// Sanity check | |
// | |
if (mLocked) { | |
DEBUG ((DEBUG_ERROR, "SmmLockBox Locked!\n")); | |
LockBoxParameterSave->Header.ReturnStatus = (UINT64)EFI_ACCESS_DENIED; | |
return; | |
} | |
CopyMem (&TempLockBoxParameterSave, LockBoxParameterSave, sizeof (EFI_SMM_LOCK_BOX_PARAMETER_SAVE)); | |
// | |
// Sanity check | |
// | |
if (!SmmIsBufferOutsideSmmValid ((UINTN)TempLockBoxParameterSave.Buffer, (UINTN)TempLockBoxParameterSave.Length)) { | |
DEBUG ((DEBUG_ERROR, "SmmLockBox Save address in SMRAM or buffer overflow!\n")); | |
LockBoxParameterSave->Header.ReturnStatus = (UINT64)EFI_ACCESS_DENIED; | |
return; | |
} | |
// | |
// The SpeculationBarrier() call here is to ensure the above range check for | |
// the CommBuffer have been completed before calling into SaveLockBox(). | |
// | |
SpeculationBarrier (); | |
// | |
// Save data | |
// | |
Status = SaveLockBox ( | |
&TempLockBoxParameterSave.Guid, | |
(VOID *)(UINTN)TempLockBoxParameterSave.Buffer, | |
(UINTN)TempLockBoxParameterSave.Length | |
); | |
LockBoxParameterSave->Header.ReturnStatus = (UINT64)Status; | |
return; | |
} | |
/** | |
Dispatch function for SMM lock box set attributes. | |
@param LockBoxParameterSetAttributes parameter of lock box set attributes | |
**/ | |
VOID | |
SmmLockBoxSetAttributes ( | |
IN EFI_SMM_LOCK_BOX_PARAMETER_SET_ATTRIBUTES *LockBoxParameterSetAttributes | |
) | |
{ | |
EFI_STATUS Status; | |
EFI_SMM_LOCK_BOX_PARAMETER_SET_ATTRIBUTES TempLockBoxParameterSetAttributes; | |
// | |
// Sanity check | |
// | |
if (mLocked) { | |
DEBUG ((DEBUG_ERROR, "SmmLockBox Locked!\n")); | |
LockBoxParameterSetAttributes->Header.ReturnStatus = (UINT64)EFI_ACCESS_DENIED; | |
return; | |
} | |
CopyMem (&TempLockBoxParameterSetAttributes, LockBoxParameterSetAttributes, sizeof (EFI_SMM_LOCK_BOX_PARAMETER_SET_ATTRIBUTES)); | |
// | |
// Update data | |
// | |
Status = SetLockBoxAttributes ( | |
&TempLockBoxParameterSetAttributes.Guid, | |
TempLockBoxParameterSetAttributes.Attributes | |
); | |
LockBoxParameterSetAttributes->Header.ReturnStatus = (UINT64)Status; | |
return; | |
} | |
/** | |
Dispatch function for SMM lock box update. | |
Caution: This function may receive untrusted input. | |
Restore buffer and length are external input, so this function will validate | |
it is in SMRAM. | |
@param LockBoxParameterUpdate parameter of lock box update | |
**/ | |
VOID | |
SmmLockBoxUpdate ( | |
IN EFI_SMM_LOCK_BOX_PARAMETER_UPDATE *LockBoxParameterUpdate | |
) | |
{ | |
EFI_STATUS Status; | |
EFI_SMM_LOCK_BOX_PARAMETER_UPDATE TempLockBoxParameterUpdate; | |
// | |
// Sanity check | |
// | |
if (mLocked) { | |
DEBUG ((DEBUG_ERROR, "SmmLockBox Locked!\n")); | |
LockBoxParameterUpdate->Header.ReturnStatus = (UINT64)EFI_ACCESS_DENIED; | |
return; | |
} | |
CopyMem (&TempLockBoxParameterUpdate, LockBoxParameterUpdate, sizeof (EFI_SMM_LOCK_BOX_PARAMETER_UPDATE)); | |
// | |
// Sanity check | |
// | |
if (!SmmIsBufferOutsideSmmValid ((UINTN)TempLockBoxParameterUpdate.Buffer, (UINTN)TempLockBoxParameterUpdate.Length)) { | |
DEBUG ((DEBUG_ERROR, "SmmLockBox Update address in SMRAM or buffer overflow!\n")); | |
LockBoxParameterUpdate->Header.ReturnStatus = (UINT64)EFI_ACCESS_DENIED; | |
return; | |
} | |
// | |
// The SpeculationBarrier() call here is to ensure the above range check for | |
// the CommBuffer have been completed before calling into UpdateLockBox(). | |
// | |
SpeculationBarrier (); | |
// | |
// Update data | |
// | |
Status = UpdateLockBox ( | |
&TempLockBoxParameterUpdate.Guid, | |
(UINTN)TempLockBoxParameterUpdate.Offset, | |
(VOID *)(UINTN)TempLockBoxParameterUpdate.Buffer, | |
(UINTN)TempLockBoxParameterUpdate.Length | |
); | |
LockBoxParameterUpdate->Header.ReturnStatus = (UINT64)Status; | |
return; | |
} | |
/** | |
Dispatch function for SMM lock box restore. | |
Caution: This function may receive untrusted input. | |
Restore buffer and length are external input, so this function will validate | |
it is in SMRAM. | |
@param LockBoxParameterRestore parameter of lock box restore | |
**/ | |
VOID | |
SmmLockBoxRestore ( | |
IN EFI_SMM_LOCK_BOX_PARAMETER_RESTORE *LockBoxParameterRestore | |
) | |
{ | |
EFI_STATUS Status; | |
EFI_SMM_LOCK_BOX_PARAMETER_RESTORE TempLockBoxParameterRestore; | |
CopyMem (&TempLockBoxParameterRestore, LockBoxParameterRestore, sizeof (EFI_SMM_LOCK_BOX_PARAMETER_RESTORE)); | |
// | |
// Sanity check | |
// | |
if (!SmmIsBufferOutsideSmmValid ((UINTN)TempLockBoxParameterRestore.Buffer, (UINTN)TempLockBoxParameterRestore.Length)) { | |
DEBUG ((DEBUG_ERROR, "SmmLockBox Restore address in SMRAM or buffer overflow!\n")); | |
LockBoxParameterRestore->Header.ReturnStatus = (UINT64)EFI_ACCESS_DENIED; | |
return; | |
} | |
// | |
// Restore data | |
// | |
if ((TempLockBoxParameterRestore.Length == 0) && (TempLockBoxParameterRestore.Buffer == 0)) { | |
Status = RestoreLockBox ( | |
&TempLockBoxParameterRestore.Guid, | |
NULL, | |
NULL | |
); | |
} else { | |
Status = RestoreLockBox ( | |
&TempLockBoxParameterRestore.Guid, | |
(VOID *)(UINTN)TempLockBoxParameterRestore.Buffer, | |
(UINTN *)&TempLockBoxParameterRestore.Length | |
); | |
if ((Status == EFI_BUFFER_TOO_SMALL) || (Status == EFI_SUCCESS)) { | |
// | |
// Return the actual Length value. | |
// | |
LockBoxParameterRestore->Length = TempLockBoxParameterRestore.Length; | |
} | |
} | |
LockBoxParameterRestore->Header.ReturnStatus = (UINT64)Status; | |
return; | |
} | |
/** | |
Dispatch function for SMM lock box restore all in place. | |
@param LockBoxParameterRestoreAllInPlace parameter of lock box restore all in place | |
**/ | |
VOID | |
SmmLockBoxRestoreAllInPlace ( | |
IN EFI_SMM_LOCK_BOX_PARAMETER_RESTORE_ALL_IN_PLACE *LockBoxParameterRestoreAllInPlace | |
) | |
{ | |
EFI_STATUS Status; | |
Status = RestoreAllLockBoxInPlace (); | |
LockBoxParameterRestoreAllInPlace->Header.ReturnStatus = (UINT64)Status; | |
return; | |
} | |
/** | |
Dispatch function for a Software SMI handler. | |
Caution: This function may receive untrusted input. | |
Communicate buffer and buffer size are external input, so this function will do basic validation. | |
@param DispatchHandle The unique handle assigned to this handler by SmiHandlerRegister(). | |
@param Context Points to an optional handler context which was specified when the | |
handler was registered. | |
@param CommBuffer A pointer to a collection of data in memory that will | |
be conveyed from a non-SMM environment into an SMM environment. | |
@param CommBufferSize The size of the CommBuffer. | |
@retval EFI_SUCCESS Command is handled successfully. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
SmmLockBoxHandler ( | |
IN EFI_HANDLE DispatchHandle, | |
IN CONST VOID *Context OPTIONAL, | |
IN OUT VOID *CommBuffer OPTIONAL, | |
IN OUT UINTN *CommBufferSize OPTIONAL | |
) | |
{ | |
EFI_SMM_LOCK_BOX_PARAMETER_HEADER *LockBoxParameterHeader; | |
UINTN TempCommBufferSize; | |
DEBUG ((DEBUG_INFO, "SmmLockBox SmmLockBoxHandler Enter\n")); | |
// | |
// If input is invalid, stop processing this SMI | |
// | |
if ((CommBuffer == NULL) || (CommBufferSize == NULL)) { | |
return EFI_SUCCESS; | |
} | |
TempCommBufferSize = *CommBufferSize; | |
// | |
// Sanity check | |
// | |
if (TempCommBufferSize < sizeof (EFI_SMM_LOCK_BOX_PARAMETER_HEADER)) { | |
DEBUG ((DEBUG_ERROR, "SmmLockBox Command Buffer Size invalid!\n")); | |
return EFI_SUCCESS; | |
} | |
if (!SmmIsBufferOutsideSmmValid ((UINTN)CommBuffer, TempCommBufferSize)) { | |
DEBUG ((DEBUG_ERROR, "SmmLockBox Command Buffer in SMRAM or overflow!\n")); | |
return EFI_SUCCESS; | |
} | |
LockBoxParameterHeader = (EFI_SMM_LOCK_BOX_PARAMETER_HEADER *)((UINTN)CommBuffer); | |
LockBoxParameterHeader->ReturnStatus = (UINT64)-1; | |
DEBUG ((DEBUG_INFO, "SmmLockBox LockBoxParameterHeader - %x\n", (UINTN)LockBoxParameterHeader)); | |
DEBUG ((DEBUG_INFO, "SmmLockBox Command - %x\n", (UINTN)LockBoxParameterHeader->Command)); | |
switch (LockBoxParameterHeader->Command) { | |
case EFI_SMM_LOCK_BOX_COMMAND_SAVE: | |
if (TempCommBufferSize < sizeof (EFI_SMM_LOCK_BOX_PARAMETER_SAVE)) { | |
DEBUG ((DEBUG_ERROR, "SmmLockBox Command Buffer Size for SAVE invalid!\n")); | |
break; | |
} | |
SmmLockBoxSave ((EFI_SMM_LOCK_BOX_PARAMETER_SAVE *)(UINTN)LockBoxParameterHeader); | |
break; | |
case EFI_SMM_LOCK_BOX_COMMAND_UPDATE: | |
if (TempCommBufferSize < sizeof (EFI_SMM_LOCK_BOX_PARAMETER_UPDATE)) { | |
DEBUG ((DEBUG_ERROR, "SmmLockBox Command Buffer Size for UPDATE invalid!\n")); | |
break; | |
} | |
SmmLockBoxUpdate ((EFI_SMM_LOCK_BOX_PARAMETER_UPDATE *)(UINTN)LockBoxParameterHeader); | |
break; | |
case EFI_SMM_LOCK_BOX_COMMAND_RESTORE: | |
if (TempCommBufferSize < sizeof (EFI_SMM_LOCK_BOX_PARAMETER_RESTORE)) { | |
DEBUG ((DEBUG_ERROR, "SmmLockBox Command Buffer Size for RESTORE invalid!\n")); | |
break; | |
} | |
SmmLockBoxRestore ((EFI_SMM_LOCK_BOX_PARAMETER_RESTORE *)(UINTN)LockBoxParameterHeader); | |
break; | |
case EFI_SMM_LOCK_BOX_COMMAND_SET_ATTRIBUTES: | |
if (TempCommBufferSize < sizeof (EFI_SMM_LOCK_BOX_PARAMETER_SET_ATTRIBUTES)) { | |
DEBUG ((DEBUG_ERROR, "SmmLockBox Command Buffer Size for SET_ATTRIBUTES invalid!\n")); | |
break; | |
} | |
SmmLockBoxSetAttributes ((EFI_SMM_LOCK_BOX_PARAMETER_SET_ATTRIBUTES *)(UINTN)LockBoxParameterHeader); | |
break; | |
case EFI_SMM_LOCK_BOX_COMMAND_RESTORE_ALL_IN_PLACE: | |
if (TempCommBufferSize < sizeof (EFI_SMM_LOCK_BOX_PARAMETER_RESTORE_ALL_IN_PLACE)) { | |
DEBUG ((DEBUG_ERROR, "SmmLockBox Command Buffer Size for RESTORE_ALL_IN_PLACE invalid!\n")); | |
break; | |
} | |
SmmLockBoxRestoreAllInPlace ((EFI_SMM_LOCK_BOX_PARAMETER_RESTORE_ALL_IN_PLACE *)(UINTN)LockBoxParameterHeader); | |
break; | |
default: | |
DEBUG ((DEBUG_ERROR, "SmmLockBox Command invalid!\n")); | |
break; | |
} | |
LockBoxParameterHeader->Command = (UINT32)-1; | |
DEBUG ((DEBUG_INFO, "SmmLockBox SmmLockBoxHandler Exit\n")); | |
return EFI_SUCCESS; | |
} | |
/** | |
Smm Ready To Lock event notification handler. | |
It sets a flag indicating that SMRAM has been locked. | |
@param[in] Protocol Points to the protocol's unique identifier. | |
@param[in] Interface Points to the interface instance. | |
@param[in] Handle The handle on which the interface was installed. | |
@retval EFI_SUCCESS Notification handler runs successfully. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
SmmReadyToLockEventNotify ( | |
IN CONST EFI_GUID *Protocol, | |
IN VOID *Interface, | |
IN EFI_HANDLE Handle | |
) | |
{ | |
mLocked = TRUE; | |
return EFI_SUCCESS; | |
} | |
/** | |
Entry Point for LockBox SMM driver. | |
@param[in] ImageHandle Image handle of this driver. | |
@param[in] SystemTable A Pointer to the EFI System Table. | |
@retval EFI_SUCEESS | |
@return Others Some error occurs. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
SmmLockBoxEntryPoint ( | |
IN EFI_HANDLE ImageHandle, | |
IN EFI_SYSTEM_TABLE *SystemTable | |
) | |
{ | |
EFI_STATUS Status; | |
EFI_HANDLE DispatchHandle; | |
VOID *Registration; | |
// | |
// Register LockBox communication handler | |
// | |
Status = gSmst->SmiHandlerRegister ( | |
SmmLockBoxHandler, | |
&gEfiSmmLockBoxCommunicationGuid, | |
&DispatchHandle | |
); | |
ASSERT_EFI_ERROR (Status); | |
// | |
// Register SMM Ready To Lock Protocol notification | |
// | |
Status = gSmst->SmmRegisterProtocolNotify ( | |
&gEfiSmmReadyToLockProtocolGuid, | |
SmmReadyToLockEventNotify, | |
&Registration | |
); | |
ASSERT_EFI_ERROR (Status); | |
// | |
// Install NULL to DXE data base as notify | |
// | |
ImageHandle = NULL; | |
Status = gBS->InstallProtocolInterface ( | |
&ImageHandle, | |
&gEfiLockBoxProtocolGuid, | |
EFI_NATIVE_INTERFACE, | |
NULL | |
); | |
ASSERT_EFI_ERROR (Status); | |
return Status; | |
} |