• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
FileFromFv(IN CONST EFI_DEVICE_PATH_PROTOCOL * File)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 *
LookupImage(IN CONST EFI_DEVICE_PATH_PROTOCOL * ImageDevicePath,IN BOOLEAN BootOption)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
QueueImage(IN CONST EFI_DEVICE_PATH_PROTOCOL * ImageDevicePath,IN BOOLEAN BootOption)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
GetDefferedImageInfo(IN EFI_DEFERRED_IMAGE_LOAD_PROTOCOL * This,IN UINTN ImageIndex,OUT EFI_DEVICE_PATH_PROTOCOL ** ImageDevicePath,OUT VOID ** Image,OUT UINTN * ImageSize,OUT BOOLEAN * BootOption)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
EndOfDxe(IN EFI_EVENT Event,IN VOID * Context)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
DxeSmmReadyToLock(IN EFI_EVENT Event,IN VOID * Context)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
Defer3rdPartyImageLoad(IN CONST EFI_DEVICE_PATH_PROTOCOL * File,IN BOOLEAN BootPolicy)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
Defer3rdPartyImageLoadInitialize(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