]> git.proxmox.com Git - mirror_edk2.git/blobdiff - SecurityPkg/VariableAuthenticated/RuntimeDxe/VariableSmmRuntimeDxe.c
MdeModulePkg and SecurityPkg Variable: Enhance code to use the new variable data...
[mirror_edk2.git] / SecurityPkg / VariableAuthenticated / RuntimeDxe / VariableSmmRuntimeDxe.c
index cdd407d66bea70b3b75dcd2ed992c916b80b8f0f..f550c67beec62162edc3e14eaf90e04131c98435 100644 (file)
@@ -29,6 +29,7 @@ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
 #include <Protocol/Variable.h>\r
 #include <Protocol/SmmCommunication.h>\r
 #include <Protocol/SmmVariable.h>\r
+#include <Protocol/VariableLock.h>\r
 \r
 #include <Library/UefiBootServicesTableLib.h>\r
 #include <Library/UefiRuntimeServicesTableLib.h>\r
@@ -52,7 +53,23 @@ EFI_SMM_COMMUNICATION_PROTOCOL  *mSmmCommunication          = NULL;
 UINT8                           *mVariableBuffer            = NULL;\r
 UINT8                           *mVariableBufferPhysical    = NULL;\r
 UINTN                            mVariableBufferSize;\r
+UINTN                            mVariableBufferPayloadSize;\r
 EFI_LOCK                         mVariableServicesLock;\r
+EDKII_VARIABLE_LOCK_PROTOCOL     mVariableLock;\r
+\r
+/**\r
+  SecureBoot Hook for SetVariable.\r
+\r
+  @param[in] VariableName                 Name of Variable to be found.\r
+  @param[in] VendorGuid                   Variable vendor GUID.\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+SecureBootHook (\r
+  IN CHAR16                                 *VariableName,\r
+  IN EFI_GUID                               *VendorGuid\r
+  );\r
 \r
 /**\r
   Acquires lock only at boot time. Simply returns at runtime.\r
@@ -172,6 +189,73 @@ SendCommunicateBuffer (
   return  SmmVariableFunctionHeader->ReturnStatus;\r
 }\r
 \r
+/**\r
+  Mark a variable that will become read-only after leaving the DXE phase of execution.\r
+\r
+  @param[in] This          The VARIABLE_LOCK_PROTOCOL instance.\r
+  @param[in] VariableName  A pointer to the variable name that will be made read-only subsequently.\r
+  @param[in] VendorGuid    A pointer to the vendor GUID that will be made read-only subsequently.\r
+\r
+  @retval EFI_SUCCESS           The variable specified by the VariableName and the VendorGuid was marked\r
+                                as pending to be read-only.\r
+  @retval EFI_INVALID_PARAMETER VariableName or VendorGuid is NULL.\r
+                                Or VariableName is an empty string.\r
+  @retval EFI_ACCESS_DENIED     EFI_END_OF_DXE_EVENT_GROUP_GUID or EFI_EVENT_GROUP_READY_TO_BOOT has\r
+                                already been signaled.\r
+  @retval EFI_OUT_OF_RESOURCES  There is not enough resource to hold the lock request.\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+VariableLockRequestToLock (\r
+  IN CONST EDKII_VARIABLE_LOCK_PROTOCOL *This,\r
+  IN       CHAR16                       *VariableName,\r
+  IN       EFI_GUID                     *VendorGuid\r
+  )\r
+{\r
+  EFI_STATUS                                Status;\r
+  UINTN                                     VariableNameSize;\r
+  UINTN                                     PayloadSize;\r
+  SMM_VARIABLE_COMMUNICATE_LOCK_VARIABLE    *VariableToLock;\r
+\r
+  if (VariableName == NULL || VariableName[0] == 0 || VendorGuid == NULL) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  VariableNameSize = StrSize (VariableName);\r
+\r
+  //\r
+  // If VariableName exceeds SMM payload limit. Return failure\r
+  //\r
+  if (VariableNameSize > mVariableBufferPayloadSize - OFFSET_OF (SMM_VARIABLE_COMMUNICATE_LOCK_VARIABLE, Name)) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  AcquireLockOnlyAtBootTime(&mVariableServicesLock);\r
+\r
+  //\r
+  // Init the communicate buffer. The buffer data size is:\r
+  // SMM_COMMUNICATE_HEADER_SIZE + SMM_VARIABLE_COMMUNICATE_HEADER_SIZE + PayloadSize.\r
+  //\r
+  PayloadSize = OFFSET_OF (SMM_VARIABLE_COMMUNICATE_LOCK_VARIABLE, Name) + VariableNameSize;\r
+  Status = InitCommunicateBuffer ((VOID **) &VariableToLock, PayloadSize, SMM_VARIABLE_FUNCTION_LOCK_VARIABLE);\r
+  if (EFI_ERROR (Status)) {\r
+    goto Done;\r
+  }\r
+  ASSERT (VariableToLock != NULL);\r
+\r
+  CopyGuid (&VariableToLock->Guid, VendorGuid);\r
+  VariableToLock->NameSize = VariableNameSize;\r
+  CopyMem (VariableToLock->Name, VariableName, VariableToLock->NameSize);\r
+\r
+  //\r
+  // Send data to SMM.\r
+  //\r
+  Status = SendCommunicateBuffer (PayloadSize);\r
+\r
+Done:\r
+  ReleaseLockOnlyAtBootTime (&mVariableServicesLock);\r
+  return Status;\r
+}\r
 \r
 /**\r
   This code finds variable in storage blocks (Volatile or Non-Volatile).\r
@@ -205,7 +289,6 @@ RuntimeServiceGetVariable (
   EFI_STATUS                                Status;\r
   UINTN                                     PayloadSize;\r
   SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE  *SmmVariableHeader;\r
-  UINTN                                     SmmCommBufPayloadSize;\r
   UINTN                                     TempDataSize;\r
   UINTN                                     VariableNameSize;\r
 \r
@@ -217,17 +300,13 @@ RuntimeServiceGetVariable (
     return EFI_INVALID_PARAMETER;\r
   }\r
 \r
-  //\r
-  // SMM Communication Buffer max payload size\r
-  //\r
-  SmmCommBufPayloadSize = mVariableBufferSize - (SMM_COMMUNICATE_HEADER_SIZE + SMM_VARIABLE_COMMUNICATE_HEADER_SIZE);\r
   TempDataSize          = *DataSize;\r
   VariableNameSize      = StrSize (VariableName);\r
 \r
   //\r
   // If VariableName exceeds SMM payload limit. Return failure\r
   //\r
-  if (VariableNameSize > SmmCommBufPayloadSize - OFFSET_OF (SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE, Name)) {\r
+  if (VariableNameSize > mVariableBufferPayloadSize - OFFSET_OF (SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE, Name)) {\r
     return EFI_INVALID_PARAMETER;\r
   }\r
 \r
@@ -237,11 +316,11 @@ RuntimeServiceGetVariable (
   // Init the communicate buffer. The buffer data size is:\r
   // SMM_COMMUNICATE_HEADER_SIZE + SMM_VARIABLE_COMMUNICATE_HEADER_SIZE + PayloadSize.\r
   //\r
-  if (TempDataSize > SmmCommBufPayloadSize - OFFSET_OF (SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE, Name) - VariableNameSize) {\r
+  if (TempDataSize > mVariableBufferPayloadSize - OFFSET_OF (SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE, Name) - VariableNameSize) {\r
     //\r
     // If output data buffer exceed SMM payload limit. Trim output buffer to SMM payload size\r
     //\r
-    TempDataSize = SmmCommBufPayloadSize - OFFSET_OF (SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE, Name) - VariableNameSize;\r
+    TempDataSize = mVariableBufferPayloadSize - OFFSET_OF (SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE, Name) - VariableNameSize;\r
   }\r
   PayloadSize = OFFSET_OF (SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE, Name) + VariableNameSize + TempDataSize;\r
 \r
@@ -316,7 +395,6 @@ RuntimeServiceGetNextVariableName (
   EFI_STATUS                                      Status;\r
   UINTN                                           PayloadSize;\r
   SMM_VARIABLE_COMMUNICATE_GET_NEXT_VARIABLE_NAME *SmmGetNextVariableName;\r
-  UINTN                                           SmmCommBufPayloadSize;\r
   UINTN                                           OutVariableNameSize;\r
   UINTN                                           InVariableNameSize;\r
 \r
@@ -324,17 +402,13 @@ RuntimeServiceGetNextVariableName (
     return EFI_INVALID_PARAMETER;\r
   }\r
 \r
-  //\r
-  // SMM Communication Buffer max payload size\r
-  //\r
-  SmmCommBufPayloadSize = mVariableBufferSize - (SMM_COMMUNICATE_HEADER_SIZE + SMM_VARIABLE_COMMUNICATE_HEADER_SIZE);\r
   OutVariableNameSize   = *VariableNameSize;\r
   InVariableNameSize    = StrSize (VariableName);\r
 \r
   //\r
   // If input string exceeds SMM payload limit. Return failure\r
   //\r
-  if (InVariableNameSize > SmmCommBufPayloadSize - OFFSET_OF (SMM_VARIABLE_COMMUNICATE_GET_NEXT_VARIABLE_NAME, Name)) {\r
+  if (InVariableNameSize > mVariableBufferPayloadSize - OFFSET_OF (SMM_VARIABLE_COMMUNICATE_GET_NEXT_VARIABLE_NAME, Name)) {\r
     return EFI_INVALID_PARAMETER;\r
   }\r
 \r
@@ -344,11 +418,11 @@ RuntimeServiceGetNextVariableName (
   // Init the communicate buffer. The buffer data size is:\r
   // SMM_COMMUNICATE_HEADER_SIZE + SMM_VARIABLE_COMMUNICATE_HEADER_SIZE + PayloadSize.\r
   //\r
-  if (OutVariableNameSize > SmmCommBufPayloadSize - OFFSET_OF (SMM_VARIABLE_COMMUNICATE_GET_NEXT_VARIABLE_NAME, Name)) {\r
+  if (OutVariableNameSize > mVariableBufferPayloadSize - OFFSET_OF (SMM_VARIABLE_COMMUNICATE_GET_NEXT_VARIABLE_NAME, Name)) {\r
     //\r
     // If output buffer exceed SMM payload limit. Trim output buffer to SMM payload size\r
     //\r
-    OutVariableNameSize = SmmCommBufPayloadSize - OFFSET_OF (SMM_VARIABLE_COMMUNICATE_GET_NEXT_VARIABLE_NAME, Name);\r
+    OutVariableNameSize = mVariableBufferPayloadSize - OFFSET_OF (SMM_VARIABLE_COMMUNICATE_GET_NEXT_VARIABLE_NAME, Name);\r
   }\r
   //\r
   // Payload should be Guid + NameSize + MAX of Input & Output buffer\r
@@ -448,21 +522,13 @@ RuntimeServiceSetVariable (
     return EFI_INVALID_PARAMETER;\r
   }\r
 \r
-  if (DataSize >= mVariableBufferSize) {\r
-    //\r
-    // DataSize may be near MAX_ADDRESS incorrectly, this can cause the computed PayLoadSize to\r
-    // overflow to a small value and pass the check in InitCommunicateBuffer().\r
-    // To protect against this vulnerability, return EFI_INVALID_PARAMETER if DataSize is >= mVariableBufferSize.\r
-    // And there will be further check to ensure the total size is also not > mVariableBufferSize.\r
-    //\r
-    return EFI_INVALID_PARAMETER;\r
-  }\r
   VariableNameSize      = StrSize (VariableName);\r
 \r
-  if ((UINTN)(~0) - VariableNameSize < OFFSET_OF (SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE, Name) + DataSize) {\r
-    //\r
-    // Prevent PayloadSize overflow\r
-    //\r
+  //\r
+  // If VariableName or DataSize exceeds SMM payload limit. Return failure\r
+  //\r
+  if ((VariableNameSize > mVariableBufferPayloadSize - OFFSET_OF (SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE, Name)) ||\r
+      (DataSize > mVariableBufferPayloadSize - OFFSET_OF (SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE, Name) - VariableNameSize)){\r
     return EFI_INVALID_PARAMETER;\r
   }\r
 \r
@@ -493,6 +559,15 @@ RuntimeServiceSetVariable (
 \r
 Done:\r
   ReleaseLockOnlyAtBootTime (&mVariableServicesLock);\r
+\r
+  if (!EfiAtRuntime ()) {\r
+    if (!EFI_ERROR (Status)) {\r
+      SecureBootHook (\r
+        VariableName,\r
+        VendorGuid\r
+        );\r
+    }\r
+  }\r
   return Status;\r
 }\r
 \r
@@ -672,10 +747,11 @@ SmmVariableReady (
   ASSERT_EFI_ERROR (Status);\r
   \r
   //\r
-  // Allocate memory for variable store.\r
+  // Allocate memory for variable communicate buffer.\r
   //\r
-  mVariableBufferSize  = SMM_COMMUNICATE_HEADER_SIZE + SMM_VARIABLE_COMMUNICATE_HEADER_SIZE;\r
-  mVariableBufferSize += MAX (PcdGet32 (PcdMaxVariableSize), PcdGet32 (PcdMaxHardwareErrorVariableSize));\r
+  mVariableBufferPayloadSize = MAX (PcdGet32 (PcdMaxVariableSize), PcdGet32 (PcdMaxHardwareErrorVariableSize)) +\r
+                               OFFSET_OF (SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE, Name) - sizeof (VARIABLE_HEADER);\r
+  mVariableBufferSize  = SMM_COMMUNICATE_HEADER_SIZE + SMM_VARIABLE_COMMUNICATE_HEADER_SIZE + mVariableBufferPayloadSize;\r
   mVariableBuffer      = AllocateRuntimePool (mVariableBufferSize);\r
   ASSERT (mVariableBuffer != NULL);\r
 \r
@@ -756,6 +832,7 @@ VariableSmmRuntimeInitialize (
   IN EFI_SYSTEM_TABLE                       *SystemTable\r
   )\r
 {\r
+  EFI_STATUS                                Status;\r
   VOID                                      *SmmVariableRegistration;\r
   VOID                                      *SmmVariableWriteRegistration;\r
   EFI_EVENT                                 OnReadyToBootEvent;\r
@@ -763,6 +840,15 @@ VariableSmmRuntimeInitialize (
 \r
   EfiInitializeLock (&mVariableServicesLock, TPL_NOTIFY);\r
 \r
+  mVariableLock.RequestToLock = VariableLockRequestToLock;\r
+  Status = gBS->InstallMultipleProtocolInterfaces (\r
+                  &mHandle,\r
+                  &gEdkiiVariableLockProtocolGuid,\r
+                  &mVariableLock,\r
+                  NULL\r
+                  );\r
+  ASSERT_EFI_ERROR (Status);\r
+\r
   //\r
   // Smm variable service is ready\r
   //\r