]> git.proxmox.com Git - mirror_edk2.git/blobdiff - MdeModulePkg/Universal/SecurityStubDxe/Defer3rdPartyImageLoad.c
MdeModulePkg/SecurityStubDxe: Defer 3rd party image before EndOfDxe
[mirror_edk2.git] / MdeModulePkg / Universal / SecurityStubDxe / Defer3rdPartyImageLoad.c
diff --git a/MdeModulePkg/Universal/SecurityStubDxe/Defer3rdPartyImageLoad.c b/MdeModulePkg/Universal/SecurityStubDxe/Defer3rdPartyImageLoad.c
new file mode 100644 (file)
index 0000000..ca45d56
--- /dev/null
@@ -0,0 +1,356 @@
+/** @file\r
+  Implement defer image load services for user identification in UEFI2.2.\r
+\r
+Copyright (c) 2016, Intel Corporation. All rights reserved.<BR>\r
+This program and the accompanying materials\r
+are licensed and made available under the terms and conditions of the BSD License\r
+which accompanies this distribution.  The full text of the license may be found at\r
+http://opensource.org/licenses/bsd-license.php\r
+\r
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+#include "Defer3rdPartyImageLoad.h"\r
+\r
+//\r
+// The structure to save the deferred 3rd party image information.\r
+//\r
+typedef struct {\r
+  EFI_DEVICE_PATH_PROTOCOL          *ImageDevicePath;\r
+  BOOLEAN                           BootOption;\r
+  BOOLEAN                           Loaded;\r
+} DEFERRED_3RD_PARTY_IMAGE_INFO;\r
+\r
+//\r
+// The table to save the deferred 3rd party image item.\r
+//\r
+typedef struct {\r
+  UINTN                             Count;         ///< deferred 3rd party image count\r
+  DEFERRED_3RD_PARTY_IMAGE_INFO     *ImageInfo;    ///< deferred 3rd party image item\r
+} DEFERRED_3RD_PARTY_IMAGE_TABLE;\r
+\r
+BOOLEAN                          mEndOfDxe                   = FALSE;\r
+DEFERRED_3RD_PARTY_IMAGE_TABLE   mDeferred3rdPartyImage = {\r
+  0,       // Deferred image count\r
+  NULL     // The deferred image info\r
+};\r
+\r
+EFI_DEFERRED_IMAGE_LOAD_PROTOCOL mDeferredImageLoad   = {\r
+  GetDefferedImageInfo\r
+};\r
+\r
+/**\r
+  Return whether the file comes from FV.\r
+\r
+  @param[in]    File    This is a pointer to the device path of the file\r
+                        that is being dispatched.\r
+\r
+  @retval TRUE  File comes from FV.\r
+  @retval FALSE File doesn't come from FV.\r
+**/\r
+BOOLEAN\r
+FileFromFv (\r
+  IN  CONST EFI_DEVICE_PATH_PROTOCOL   *File\r
+  )\r
+{\r
+  EFI_STATUS                        Status;\r
+  EFI_HANDLE                        DeviceHandle;\r
+  EFI_DEVICE_PATH_PROTOCOL          *TempDevicePath;\r
+\r
+  //\r
+  // First check to see if File is from a Firmware Volume\r
+  //\r
+  DeviceHandle   = NULL;\r
+  TempDevicePath = (EFI_DEVICE_PATH_PROTOCOL *) File;\r
+  Status = gBS->LocateDevicePath (\r
+                  &gEfiFirmwareVolume2ProtocolGuid,\r
+                  &TempDevicePath,\r
+                  &DeviceHandle\r
+                  );\r
+  if (!EFI_ERROR (Status)) {\r
+    Status = gBS->OpenProtocol (\r
+                    DeviceHandle,\r
+                    &gEfiFirmwareVolume2ProtocolGuid,\r
+                    NULL,\r
+                    NULL,\r
+                    NULL,\r
+                    EFI_OPEN_PROTOCOL_TEST_PROTOCOL\r
+                    );\r
+    if (!EFI_ERROR (Status)) {\r
+      return TRUE;\r
+    }\r
+  }\r
+\r
+  return FALSE;\r
+}\r
+\r
+/**\r
+  Find the deferred image which matches the device path.\r
+\r
+  @param[in]  ImageDevicePath  A pointer to the device path of a image.\r
+  @param[in]  BootOption       Whether the image is a boot option.\r
+\r
+  @return Pointer to the found deferred image or NULL if not found.\r
+**/\r
+DEFERRED_3RD_PARTY_IMAGE_INFO *\r
+LookupImage (\r
+  IN  CONST EFI_DEVICE_PATH_PROTOCOL    *ImageDevicePath,\r
+  IN        BOOLEAN                     BootOption\r
+  )\r
+{\r
+  UINTN                                 Index;\r
+  UINTN                                 DevicePathSize;\r
+\r
+  DevicePathSize = GetDevicePathSize (ImageDevicePath);\r
+\r
+  for (Index = 0; Index < mDeferred3rdPartyImage.Count; Index++) {\r
+    if (CompareMem (ImageDevicePath, mDeferred3rdPartyImage.ImageInfo[Index].ImageDevicePath, DevicePathSize) == 0) {\r
+      ASSERT (mDeferred3rdPartyImage.ImageInfo[Index].BootOption == BootOption);\r
+      return &mDeferred3rdPartyImage.ImageInfo[Index];\r
+    }\r
+  }\r
+\r
+  return NULL;\r
+}\r
+\r
+/**\r
+  Add the image info to a deferred image list.\r
+\r
+  @param[in]  ImageDevicePath  A pointer to the device path of a image.\r
+  @param[in]  BootOption       Whether the image is a boot option.\r
+\r
+**/\r
+VOID\r
+QueueImage (\r
+  IN  CONST EFI_DEVICE_PATH_PROTOCOL    *ImageDevicePath,\r
+  IN        BOOLEAN                     BootOption\r
+  )\r
+{\r
+  DEFERRED_3RD_PARTY_IMAGE_INFO         *ImageInfo;\r
+\r
+  //\r
+  // Expand memory for the new deferred image.\r
+  //\r
+  ImageInfo = ReallocatePool (\r
+                mDeferred3rdPartyImage.Count * sizeof (DEFERRED_3RD_PARTY_IMAGE_INFO),\r
+                (mDeferred3rdPartyImage.Count + 1) * sizeof (DEFERRED_3RD_PARTY_IMAGE_INFO),\r
+                mDeferred3rdPartyImage.ImageInfo\r
+  );\r
+  if (ImageInfo == NULL) {\r
+    return;\r
+  }\r
+  mDeferred3rdPartyImage.ImageInfo = ImageInfo;\r
+\r
+  //\r
+  // Save the deferred image information.\r
+  //\r
+  ImageInfo = &mDeferred3rdPartyImage.ImageInfo[mDeferred3rdPartyImage.Count];\r
+  ImageInfo->ImageDevicePath = DuplicateDevicePath (ImageDevicePath);\r
+  if (ImageInfo->ImageDevicePath == NULL) {\r
+    return;\r
+  }\r
+  ImageInfo->BootOption = BootOption;\r
+  ImageInfo->Loaded     = FALSE;\r
+  mDeferred3rdPartyImage.Count++;\r
+}\r
+\r
+\r
+/**\r
+  Returns information about a deferred image.\r
+\r
+  This function returns information about a single deferred image. The deferred images are\r
+  numbered consecutively, starting with 0.  If there is no image which corresponds to\r
+  ImageIndex, then EFI_NOT_FOUND is returned. All deferred images may be returned by\r
+  iteratively calling this function until EFI_NOT_FOUND is returned.\r
+  Image may be NULL and ImageSize set to 0 if the decision to defer execution was made\r
+  because of the location of the executable image, rather than its actual contents.\r
+\r
+  @param[in]  This             Points to this instance of the EFI_DEFERRED_IMAGE_LOAD_PROTOCOL.\r
+  @param[in]  ImageIndex       Zero-based index of the deferred index.\r
+  @param[out] ImageDevicePath  On return, points to a pointer to the device path of the image.\r
+                               The device path should not be freed by the caller.\r
+  @param[out] Image            On return, points to the first byte of the image or NULL if the\r
+                               image is not available. The image should not be freed by the caller\r
+                               unless LoadImage() has been successfully called.\r
+  @param[out] ImageSize        On return, the size of the image, or 0 if the image is not available.\r
+  @param[out] BootOption       On return, points to TRUE if the image was intended as a boot option\r
+                               or FALSE if it was not intended as a boot option.\r
+\r
+  @retval EFI_SUCCESS           Image information returned successfully.\r
+  @retval EFI_NOT_FOUND         ImageIndex does not refer to a valid image.\r
+  @retval EFI_INVALID_PARAMETER ImageDevicePath is NULL or Image is NULL or ImageSize is NULL or\r
+                                BootOption is NULL.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+GetDefferedImageInfo (\r
+  IN     EFI_DEFERRED_IMAGE_LOAD_PROTOCOL  *This,\r
+  IN     UINTN                             ImageIndex,\r
+     OUT EFI_DEVICE_PATH_PROTOCOL          **ImageDevicePath,\r
+     OUT VOID                              **Image,\r
+     OUT UINTN                             *ImageSize,\r
+     OUT BOOLEAN                           *BootOption\r
+  )\r
+{\r
+  UINTN                                    Index;\r
+  UINTN                                    NewCount;\r
+\r
+  if ((This == NULL) || (ImageSize == NULL) || (Image == NULL)) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  if ((ImageDevicePath == NULL) || (BootOption == NULL)) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  //\r
+  // Remove the loaded images from the defer list in the first call.\r
+  //\r
+  if (ImageIndex == 0) {\r
+    NewCount = 0;\r
+    for (Index = 0; Index < mDeferred3rdPartyImage.Count; Index++) {\r
+      if (!mDeferred3rdPartyImage.ImageInfo[Index].Loaded) {\r
+        CopyMem (\r
+          &mDeferred3rdPartyImage.ImageInfo[NewCount],\r
+          &mDeferred3rdPartyImage.ImageInfo[Index],\r
+          sizeof (DEFERRED_3RD_PARTY_IMAGE_INFO)\r
+          );\r
+        NewCount++;\r
+      }\r
+    }\r
+\r
+    mDeferred3rdPartyImage.Count = NewCount;\r
+  }\r
+\r
+  if (ImageIndex >= mDeferred3rdPartyImage.Count) {\r
+    return EFI_NOT_FOUND;\r
+  }\r
+\r
+  //\r
+  // Get the request deferred image.\r
+  //\r
+  *ImageDevicePath = mDeferred3rdPartyImage.ImageInfo[ImageIndex].ImageDevicePath;\r
+  *BootOption      = mDeferred3rdPartyImage.ImageInfo[ImageIndex].BootOption;\r
+  *Image           = NULL;\r
+  *ImageSize       = 0;\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  Callback function executed when the EndOfDxe event group is signaled.\r
+\r
+  @param[in] Event      Event whose notification function is being invoked.\r
+  @param[in] Context    The pointer to the notification function's context, which\r
+                        is implementation-dependent.\r
+**/\r
+VOID\r
+EFIAPI\r
+EndOfDxe (\r
+  IN EFI_EVENT  Event,\r
+  IN VOID       *Context\r
+  )\r
+{\r
+  mEndOfDxe = TRUE;\r
+}\r
+\r
+/**\r
+  Defer the 3rd party image load and installs Deferred Image Load Protocol.\r
+\r
+  @param[in]  File                  This is a pointer to the device path of the file that\r
+                                    is being dispatched. This will optionally be used for\r
+                                    logging.\r
+  @param[in]  BootPolicy            A boot policy that was used to call LoadImage() UEFI service.\r
+\r
+  @retval EFI_SUCCESS               The file is not 3rd party image and can be loaded immediately.\r
+  @retval EFI_ACCESS_DENIED         The file is 3rd party image and needs deferred.\r
+**/\r
+EFI_STATUS\r
+Defer3rdPartyImageLoad (\r
+  IN  CONST EFI_DEVICE_PATH_PROTOCOL   *File,\r
+  IN  BOOLEAN                          BootPolicy\r
+  )\r
+{\r
+  DEFERRED_3RD_PARTY_IMAGE_INFO        *ImageInfo;\r
+\r
+  //\r
+  // Ignore if File is NULL.\r
+  //\r
+  if (File == NULL) {\r
+    return EFI_SUCCESS;\r
+  }\r
+\r
+  if (FileFromFv (File)) {\r
+    return EFI_SUCCESS;\r
+  }\r
+\r
+  ImageInfo = LookupImage (File, BootPolicy);\r
+\r
+  DEBUG_CODE (\r
+    CHAR16 *DevicePathStr;\r
+    DevicePathStr = ConvertDevicePathToText (File, FALSE, FALSE);\r
+    DEBUG ((\r
+      DEBUG_INFO,\r
+      "[Security] 3rd party image[%p] %s EndOfDxe: %s.\n", ImageInfo,\r
+      mEndOfDxe ? L"can be loaded after": L"is deferred to load before",\r
+      DevicePathStr\r
+      ));\r
+    if (DevicePathStr != NULL) {\r
+      FreePool (DevicePathStr);\r
+    }\r
+    );\r
+\r
+  if (mEndOfDxe) {\r
+    //\r
+    // The image might be first time loaded after EndOfDxe,\r
+    // So ImageInfo can be NULL.\r
+    //\r
+    if (ImageInfo != NULL) {\r
+      ImageInfo->Loaded = TRUE;\r
+    }\r
+    return EFI_SUCCESS;\r
+  } else {\r
+    //\r
+    // The image might be second time loaded before EndOfDxe,\r
+    // So ImageInfo can be non-NULL.\r
+    //\r
+    if (ImageInfo == NULL) {\r
+      QueueImage (File, BootPolicy);\r
+    }\r
+    return EFI_ACCESS_DENIED;\r
+  }\r
+}\r
+\r
+/**\r
+  Installs DeferredImageLoad Protocol and listens EndOfDxe event.\r
+**/\r
+VOID\r
+Defer3rdPartyImageLoadInitialize (\r
+  VOID\r
+  )\r
+{\r
+  EFI_STATUS                           Status;\r
+  EFI_HANDLE                           Handle;\r
+  EFI_EVENT                            Event;\r
+\r
+  Handle = NULL;\r
+  Status = gBS->InstallMultipleProtocolInterfaces (\r
+                  &Handle,\r
+                  &gEfiDeferredImageLoadProtocolGuid,\r
+                  &mDeferredImageLoad,\r
+                  NULL\r
+                  );\r
+  ASSERT_EFI_ERROR (Status);\r
+\r
+  Status = gBS->CreateEventEx (\r
+                  EVT_NOTIFY_SIGNAL,\r
+                  TPL_CALLBACK,\r
+                  EndOfDxe,\r
+                  NULL,\r
+                  &gEfiEndOfDxeEventGroupGuid,\r
+                  &Event\r
+                  );\r
+  ASSERT_EFI_ERROR (Status);\r
+}\r