Home | History | Annotate | Download | only in SecurityStubDxe
      1 /** @file
      2   Implement defer image load services for user identification in UEFI2.2.
      3 
      4 Copyright (c) 2016, Intel Corporation. All rights reserved.<BR>
      5 This program and the accompanying materials
      6 are licensed and made available under the terms and conditions of the BSD License
      7 which accompanies this distribution.  The full text of the license may be found at
      8 http://opensource.org/licenses/bsd-license.php
      9 
     10 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
     11 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
     12 
     13 **/
     14 #include "Defer3rdPartyImageLoad.h"
     15 
     16 //
     17 // The structure to save the deferred 3rd party image information.
     18 //
     19 typedef struct {
     20   EFI_DEVICE_PATH_PROTOCOL          *ImageDevicePath;
     21   BOOLEAN                           BootOption;
     22   BOOLEAN                           Loaded;
     23 } DEFERRED_3RD_PARTY_IMAGE_INFO;
     24 
     25 //
     26 // The table to save the deferred 3rd party image item.
     27 //
     28 typedef struct {
     29   UINTN                             Count;         ///< deferred 3rd party image count
     30   DEFERRED_3RD_PARTY_IMAGE_INFO     *ImageInfo;    ///< deferred 3rd party image item
     31 } DEFERRED_3RD_PARTY_IMAGE_TABLE;
     32 
     33 BOOLEAN                          mImageLoadedAfterEndOfDxe   = FALSE;
     34 BOOLEAN                          mEndOfDxe                   = FALSE;
     35 DEFERRED_3RD_PARTY_IMAGE_TABLE   mDeferred3rdPartyImage = {
     36   0,       // Deferred image count
     37   NULL     // The deferred image info
     38 };
     39 
     40 EFI_DEFERRED_IMAGE_LOAD_PROTOCOL mDeferredImageLoad   = {
     41   GetDefferedImageInfo
     42 };
     43 
     44 /**
     45   Return whether the file comes from FV.
     46 
     47   @param[in]    File    This is a pointer to the device path of the file
     48                         that is being dispatched.
     49 
     50   @retval TRUE  File comes from FV.
     51   @retval FALSE File doesn't come from FV.
     52 **/
     53 BOOLEAN
     54 FileFromFv (
     55   IN  CONST EFI_DEVICE_PATH_PROTOCOL   *File
     56   )
     57 {
     58   EFI_STATUS                        Status;
     59   EFI_HANDLE                        DeviceHandle;
     60   EFI_DEVICE_PATH_PROTOCOL          *TempDevicePath;
     61 
     62   //
     63   // First check to see if File is from a Firmware Volume
     64   //
     65   DeviceHandle   = NULL;
     66   TempDevicePath = (EFI_DEVICE_PATH_PROTOCOL *) File;
     67   Status = gBS->LocateDevicePath (
     68                   &gEfiFirmwareVolume2ProtocolGuid,
     69                   &TempDevicePath,
     70                   &DeviceHandle
     71                   );
     72   if (!EFI_ERROR (Status)) {
     73     Status = gBS->OpenProtocol (
     74                     DeviceHandle,
     75                     &gEfiFirmwareVolume2ProtocolGuid,
     76                     NULL,
     77                     NULL,
     78                     NULL,
     79                     EFI_OPEN_PROTOCOL_TEST_PROTOCOL
     80                     );
     81     if (!EFI_ERROR (Status)) {
     82       return TRUE;
     83     }
     84   }
     85 
     86   return FALSE;
     87 }
     88 
     89 /**
     90   Find the deferred image which matches the device path.
     91 
     92   @param[in]  ImageDevicePath  A pointer to the device path of a image.
     93   @param[in]  BootOption       Whether the image is a boot option.
     94 
     95   @return Pointer to the found deferred image or NULL if not found.
     96 **/
     97 DEFERRED_3RD_PARTY_IMAGE_INFO *
     98 LookupImage (
     99   IN  CONST EFI_DEVICE_PATH_PROTOCOL    *ImageDevicePath,
    100   IN        BOOLEAN                     BootOption
    101   )
    102 {
    103   UINTN                                 Index;
    104   UINTN                                 DevicePathSize;
    105 
    106   DevicePathSize = GetDevicePathSize (ImageDevicePath);
    107 
    108   for (Index = 0; Index < mDeferred3rdPartyImage.Count; Index++) {
    109     if (CompareMem (ImageDevicePath, mDeferred3rdPartyImage.ImageInfo[Index].ImageDevicePath, DevicePathSize) == 0) {
    110       ASSERT (mDeferred3rdPartyImage.ImageInfo[Index].BootOption == BootOption);
    111       return &mDeferred3rdPartyImage.ImageInfo[Index];
    112     }
    113   }
    114 
    115   return NULL;
    116 }
    117 
    118 /**
    119   Add the image info to a deferred image list.
    120 
    121   @param[in]  ImageDevicePath  A pointer to the device path of a image.
    122   @param[in]  BootOption       Whether the image is a boot option.
    123 
    124 **/
    125 VOID
    126 QueueImage (
    127   IN  CONST EFI_DEVICE_PATH_PROTOCOL    *ImageDevicePath,
    128   IN        BOOLEAN                     BootOption
    129   )
    130 {
    131   DEFERRED_3RD_PARTY_IMAGE_INFO         *ImageInfo;
    132 
    133   //
    134   // Expand memory for the new deferred image.
    135   //
    136   ImageInfo = ReallocatePool (
    137                 mDeferred3rdPartyImage.Count * sizeof (DEFERRED_3RD_PARTY_IMAGE_INFO),
    138                 (mDeferred3rdPartyImage.Count + 1) * sizeof (DEFERRED_3RD_PARTY_IMAGE_INFO),
    139                 mDeferred3rdPartyImage.ImageInfo
    140   );
    141   if (ImageInfo == NULL) {
    142     return;
    143   }
    144   mDeferred3rdPartyImage.ImageInfo = ImageInfo;
    145 
    146   //
    147   // Save the deferred image information.
    148   //
    149   ImageInfo = &mDeferred3rdPartyImage.ImageInfo[mDeferred3rdPartyImage.Count];
    150   ImageInfo->ImageDevicePath = DuplicateDevicePath (ImageDevicePath);
    151   if (ImageInfo->ImageDevicePath == NULL) {
    152     return;
    153   }
    154   ImageInfo->BootOption = BootOption;
    155   ImageInfo->Loaded     = FALSE;
    156   mDeferred3rdPartyImage.Count++;
    157 }
    158 
    159 
    160 /**
    161   Returns information about a deferred image.
    162 
    163   This function returns information about a single deferred image. The deferred images are
    164   numbered consecutively, starting with 0.  If there is no image which corresponds to
    165   ImageIndex, then EFI_NOT_FOUND is returned. All deferred images may be returned by
    166   iteratively calling this function until EFI_NOT_FOUND is returned.
    167   Image may be NULL and ImageSize set to 0 if the decision to defer execution was made
    168   because of the location of the executable image, rather than its actual contents.
    169 
    170   @param[in]  This             Points to this instance of the EFI_DEFERRED_IMAGE_LOAD_PROTOCOL.
    171   @param[in]  ImageIndex       Zero-based index of the deferred index.
    172   @param[out] ImageDevicePath  On return, points to a pointer to the device path of the image.
    173                                The device path should not be freed by the caller.
    174   @param[out] Image            On return, points to the first byte of the image or NULL if the
    175                                image is not available. The image should not be freed by the caller
    176                                unless LoadImage() has been successfully called.
    177   @param[out] ImageSize        On return, the size of the image, or 0 if the image is not available.
    178   @param[out] BootOption       On return, points to TRUE if the image was intended as a boot option
    179                                or FALSE if it was not intended as a boot option.
    180 
    181   @retval EFI_SUCCESS           Image information returned successfully.
    182   @retval EFI_NOT_FOUND         ImageIndex does not refer to a valid image.
    183   @retval EFI_INVALID_PARAMETER ImageDevicePath is NULL or Image is NULL or ImageSize is NULL or
    184                                 BootOption is NULL.
    185 
    186 **/
    187 EFI_STATUS
    188 EFIAPI
    189 GetDefferedImageInfo (
    190   IN     EFI_DEFERRED_IMAGE_LOAD_PROTOCOL  *This,
    191   IN     UINTN                             ImageIndex,
    192      OUT EFI_DEVICE_PATH_PROTOCOL          **ImageDevicePath,
    193      OUT VOID                              **Image,
    194      OUT UINTN                             *ImageSize,
    195      OUT BOOLEAN                           *BootOption
    196   )
    197 {
    198   UINTN                                    Index;
    199   UINTN                                    NewCount;
    200 
    201   if ((This == NULL) || (ImageSize == NULL) || (Image == NULL)) {
    202     return EFI_INVALID_PARAMETER;
    203   }
    204 
    205   if ((ImageDevicePath == NULL) || (BootOption == NULL)) {
    206     return EFI_INVALID_PARAMETER;
    207   }
    208 
    209   //
    210   // Remove the loaded images from the defer list in the first call.
    211   //
    212   if (ImageIndex == 0) {
    213     NewCount = 0;
    214     for (Index = 0; Index < mDeferred3rdPartyImage.Count; Index++) {
    215       if (!mDeferred3rdPartyImage.ImageInfo[Index].Loaded) {
    216         CopyMem (
    217           &mDeferred3rdPartyImage.ImageInfo[NewCount],
    218           &mDeferred3rdPartyImage.ImageInfo[Index],
    219           sizeof (DEFERRED_3RD_PARTY_IMAGE_INFO)
    220           );
    221         NewCount++;
    222       }
    223     }
    224 
    225     mDeferred3rdPartyImage.Count = NewCount;
    226   }
    227 
    228   if (ImageIndex >= mDeferred3rdPartyImage.Count) {
    229     return EFI_NOT_FOUND;
    230   }
    231 
    232   //
    233   // Get the request deferred image.
    234   //
    235   *ImageDevicePath = mDeferred3rdPartyImage.ImageInfo[ImageIndex].ImageDevicePath;
    236   *BootOption      = mDeferred3rdPartyImage.ImageInfo[ImageIndex].BootOption;
    237   *Image           = NULL;
    238   *ImageSize       = 0;
    239 
    240   return EFI_SUCCESS;
    241 }
    242 
    243 /**
    244   Callback function executed when the EndOfDxe event group is signaled.
    245 
    246   @param[in] Event      Event whose notification function is being invoked.
    247   @param[in] Context    The pointer to the notification function's context, which
    248                         is implementation-dependent.
    249 **/
    250 VOID
    251 EFIAPI
    252 EndOfDxe (
    253   IN EFI_EVENT  Event,
    254   IN VOID       *Context
    255   )
    256 {
    257   mEndOfDxe = TRUE;
    258 }
    259 
    260 /**
    261   Event notification for gEfiDxeSmmReadyToLockProtocolGuid event.
    262 
    263   This function reports failure if any deferred image is loaded before
    264   this callback.
    265   Platform should publish ReadyToLock protocol immediately after signaling
    266   of the End of DXE Event.
    267 
    268   @param  Event                 The Event that is being processed, not used.
    269   @param  Context               Event Context, not used.
    270 
    271 **/
    272 VOID
    273 EFIAPI
    274 DxeSmmReadyToLock (
    275   IN EFI_EVENT  Event,
    276   IN VOID       *Context
    277   )
    278 {
    279   EFI_STATUS                Status;
    280   VOID                      *Interface;
    281 
    282   Status = gBS->LocateProtocol (&gEfiDxeSmmReadyToLockProtocolGuid, NULL, &Interface);
    283   if (EFI_ERROR (Status)) {
    284     return;
    285   }
    286 
    287   gBS->CloseEvent (Event);
    288 
    289   if (mImageLoadedAfterEndOfDxe) {
    290     //
    291     // Platform should not dispatch the 3rd party images after signaling EndOfDxe event
    292     // but before publishing DxeSmmReadyToLock protocol.
    293     //
    294     DEBUG ((
    295       DEBUG_ERROR,
    296       "[Security] 3rd party images must be dispatched after DxeSmmReadyToLock Protocol installation!\n"
    297       ));
    298     REPORT_STATUS_CODE (
    299       EFI_ERROR_CODE | EFI_ERROR_UNRECOVERED,
    300       (EFI_SOFTWARE_DXE_BS_DRIVER | EFI_SW_EC_ILLEGAL_SOFTWARE_STATE)
    301       );
    302     ASSERT (FALSE);
    303     CpuDeadLoop ();
    304   }
    305 }
    306 
    307 /**
    308   Defer the 3rd party image load and installs Deferred Image Load Protocol.
    309 
    310   @param[in]  File                  This is a pointer to the device path of the file that
    311                                     is being dispatched. This will optionally be used for
    312                                     logging.
    313   @param[in]  BootPolicy            A boot policy that was used to call LoadImage() UEFI service.
    314 
    315   @retval EFI_SUCCESS               The file is not 3rd party image and can be loaded immediately.
    316   @retval EFI_ACCESS_DENIED         The file is 3rd party image and needs deferred.
    317 **/
    318 EFI_STATUS
    319 Defer3rdPartyImageLoad (
    320   IN  CONST EFI_DEVICE_PATH_PROTOCOL   *File,
    321   IN  BOOLEAN                          BootPolicy
    322   )
    323 {
    324   DEFERRED_3RD_PARTY_IMAGE_INFO        *ImageInfo;
    325 
    326   //
    327   // Ignore if File is NULL.
    328   //
    329   if (File == NULL) {
    330     return EFI_SUCCESS;
    331   }
    332 
    333   if (FileFromFv (File)) {
    334     return EFI_SUCCESS;
    335   }
    336 
    337   ImageInfo = LookupImage (File, BootPolicy);
    338 
    339   DEBUG_CODE (
    340     CHAR16 *DevicePathStr;
    341     DevicePathStr = ConvertDevicePathToText (File, FALSE, FALSE);
    342     DEBUG ((
    343       DEBUG_INFO,
    344       "[Security] 3rd party image[%p] %s EndOfDxe: %s.\n", ImageInfo,
    345       mEndOfDxe ? L"can be loaded after": L"is deferred to load before",
    346       DevicePathStr
    347       ));
    348     if (DevicePathStr != NULL) {
    349       FreePool (DevicePathStr);
    350     }
    351     );
    352 
    353   if (mEndOfDxe) {
    354     mImageLoadedAfterEndOfDxe = TRUE;
    355     //
    356     // The image might be first time loaded after EndOfDxe,
    357     // So ImageInfo can be NULL.
    358     //
    359     if (ImageInfo != NULL) {
    360       ImageInfo->Loaded = TRUE;
    361     }
    362     return EFI_SUCCESS;
    363   } else {
    364     //
    365     // The image might be second time loaded before EndOfDxe,
    366     // So ImageInfo can be non-NULL.
    367     //
    368     if (ImageInfo == NULL) {
    369       QueueImage (File, BootPolicy);
    370     }
    371     return EFI_ACCESS_DENIED;
    372   }
    373 }
    374 
    375 /**
    376   Installs DeferredImageLoad Protocol and listens EndOfDxe event.
    377 **/
    378 VOID
    379 Defer3rdPartyImageLoadInitialize (
    380   VOID
    381   )
    382 {
    383   EFI_STATUS                           Status;
    384   EFI_HANDLE                           Handle;
    385   EFI_EVENT                            Event;
    386   VOID                                 *Registration;
    387 
    388   Handle = NULL;
    389   Status = gBS->InstallMultipleProtocolInterfaces (
    390                   &Handle,
    391                   &gEfiDeferredImageLoadProtocolGuid,
    392                   &mDeferredImageLoad,
    393                   NULL
    394                   );
    395   ASSERT_EFI_ERROR (Status);
    396 
    397   Status = gBS->CreateEventEx (
    398                   EVT_NOTIFY_SIGNAL,
    399                   TPL_CALLBACK,
    400                   EndOfDxe,
    401                   NULL,
    402                   &gEfiEndOfDxeEventGroupGuid,
    403                   &Event
    404                   );
    405   ASSERT_EFI_ERROR (Status);
    406 
    407   EfiCreateProtocolNotifyEvent (
    408     &gEfiDxeSmmReadyToLockProtocolGuid,
    409     TPL_CALLBACK,
    410     DxeSmmReadyToLock,
    411     NULL,
    412     &Registration
    413     );
    414 }
    415