2 Implement defer image load services for user identification in UEFI2.2.
4 Copyright (c) 2009 - 2012, 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
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.
15 #include "DxeDeferImageLoadLib.h"
18 // Handle for the Deferred Image Load Protocol instance produced by this driver.
20 EFI_HANDLE mDeferredImageHandle
= NULL
;
21 BOOLEAN mIsProtocolInstalled
= FALSE
;
22 EFI_USER_MANAGER_PROTOCOL
*mUserManager
= NULL
;
23 DEFERRED_IMAGE_TABLE mDeferredImage
= {
24 0, // Deferred image count
25 NULL
// The deferred image info
28 EFI_DEFERRED_IMAGE_LOAD_PROTOCOL gDeferredImageLoad
= {
35 @param[in] File This is a pointer to the device path of the file
36 that is being dispatched.
38 @return UINT32 Image Type
43 IN CONST EFI_DEVICE_PATH_PROTOCOL
*File
47 EFI_HANDLE DeviceHandle
;
48 EFI_DEVICE_PATH_PROTOCOL
*TempDevicePath
;
49 EFI_BLOCK_IO_PROTOCOL
*BlockIo
;
52 // First check to see if File is from a Firmware Volume
55 TempDevicePath
= (EFI_DEVICE_PATH_PROTOCOL
*)File
;
56 Status
= gBS
->LocateDevicePath (
57 &gEfiFirmwareVolume2ProtocolGuid
,
61 if (!EFI_ERROR (Status
)) {
62 Status
= gBS
->OpenProtocol (
64 &gEfiFirmwareVolume2ProtocolGuid
,
68 EFI_OPEN_PROTOCOL_TEST_PROTOCOL
70 if (!EFI_ERROR (Status
)) {
76 // Next check to see if File is from a Block I/O device
79 TempDevicePath
= (EFI_DEVICE_PATH_PROTOCOL
*)File
;
80 Status
= gBS
->LocateDevicePath (
81 &gEfiBlockIoProtocolGuid
,
85 if (!EFI_ERROR (Status
)) {
87 Status
= gBS
->OpenProtocol (
89 &gEfiBlockIoProtocolGuid
,
93 EFI_OPEN_PROTOCOL_GET_PROTOCOL
95 if (!EFI_ERROR (Status
) && BlockIo
!= NULL
) {
96 if (BlockIo
->Media
!= NULL
) {
97 if (BlockIo
->Media
->RemovableMedia
) {
99 // Block I/O is present and specifies the media is removable
101 return IMAGE_FROM_REMOVABLE_MEDIA
;
104 // Block I/O is present and specifies the media is not removable
106 return IMAGE_FROM_FIXED_MEDIA
;
113 // File is not in a Firmware Volume or on a Block I/O device, so check to see if
114 // the device path supports the Simple File System Protocol.
117 TempDevicePath
= (EFI_DEVICE_PATH_PROTOCOL
*)File
;
118 Status
= gBS
->LocateDevicePath (
119 &gEfiSimpleFileSystemProtocolGuid
,
123 if (!EFI_ERROR (Status
)) {
125 // Simple File System is present without Block I/O, so assume media is fixed.
127 return IMAGE_FROM_FIXED_MEDIA
;
131 // File is not from an FV, Block I/O or Simple File System, so the only options
132 // left are a PCI Option ROM and a Load File Protocol such as a PXE Boot from a NIC.
134 TempDevicePath
= (EFI_DEVICE_PATH_PROTOCOL
*)File
;
135 while (!IsDevicePathEndType (TempDevicePath
)) {
136 switch (DevicePathType (TempDevicePath
)) {
138 case MEDIA_DEVICE_PATH
:
139 if (DevicePathSubType (TempDevicePath
) == MEDIA_RELATIVE_OFFSET_RANGE_DP
) {
140 return IMAGE_FROM_OPTION_ROM
;
144 case MESSAGING_DEVICE_PATH
:
145 if (DevicePathSubType(TempDevicePath
) == MSG_MAC_ADDR_DP
) {
146 return IMAGE_FROM_REMOVABLE_MEDIA
;
153 TempDevicePath
= NextDevicePathNode (TempDevicePath
);
155 return IMAGE_UNKNOWN
;
160 Get current user's access right.
162 @param[out] AccessControl Points to the user's access control data, the
163 caller should free data buffer.
164 @param[in] AccessType The type of user access control.
166 @retval EFI_SUCCESS Get current user access control successfully
167 @retval others Fail to get current user access control
172 OUT EFI_USER_INFO_ACCESS_CONTROL
**AccessControl
,
177 EFI_USER_INFO_HANDLE UserInfo
;
180 EFI_USER_INFO_ACCESS_CONTROL
*Access
;
181 EFI_USER_PROFILE_HANDLE CurrentUser
;
183 EFI_USER_MANAGER_PROTOCOL
*UserManager
;
186 Status
= gBS
->LocateProtocol (
187 &gEfiUserManagerProtocolGuid
,
189 (VOID
**) &UserManager
191 if (EFI_ERROR (Status
)) {
192 return EFI_NOT_FOUND
;
196 // Get current user access information.
198 UserManager
->Current (UserManager
, &CurrentUser
);
205 // Get next user information.
207 Status
= UserManager
->GetNextInfo (UserManager
, CurrentUser
, &UserInfo
);
208 if (EFI_ERROR (Status
)) {
212 Status
= UserManager
->GetInfo (
219 if (Status
== EFI_BUFFER_TOO_SMALL
) {
223 Info
= AllocateZeroPool (InfoSize
);
224 ASSERT (Info
!= NULL
);
225 Status
= UserManager
->GetInfo (
234 if (EFI_ERROR (Status
)) {
238 ASSERT (Info
!= NULL
);
239 if (Info
->InfoType
!= EFI_USER_INFO_ACCESS_POLICY_RECORD
) {
244 // Get specified access information.
247 while (CheckLen
< Info
->InfoSize
- sizeof (EFI_USER_INFO
)) {
248 Access
= (EFI_USER_INFO_ACCESS_CONTROL
*) ((UINT8
*) (Info
+ 1) + CheckLen
);
249 if ((Access
->Type
== AccessType
)) {
250 *AccessControl
= AllocateZeroPool (Access
->Size
);
251 ASSERT (*AccessControl
!= NULL
);
252 CopyMem (*AccessControl
, Access
, Access
->Size
);
256 CheckLen
+= Access
->Size
;
263 return EFI_NOT_FOUND
;
268 Convert the '/' to '\' in the specified string.
270 @param[in, out] Str Points to the string to convert.
275 IN OUT EFI_STRING Str
281 Count
= StrSize(Str
) / 2 - 1;
288 // Convert device path string.
293 // Find the last '/'.
295 for (Index
= Count
- 1; Index
> 0; Index
--) {
296 if (Str
[Index
] == L
'/')
303 if (Str
[Index
+ 1] == L
'\\')
309 // Check previous char.
311 if ((Index
> 0) && (Str
[Index
- 1] == L
'\\')) {
312 CopyMem (&Str
[Index
- 1], &Str
[Index
], (UINTN
) ((Count
- Index
+ 1) * sizeof (CHAR16
)));
321 Check whether the DevicePath2 is identical with DevicePath1, or identical with
322 DevicePath1's child device path.
324 If DevicePath2 is identical with DevicePath1, or with DevicePath1's child device
325 path, then TRUE returned. Otherwise, FALSE is returned.
327 If DevicePath1 is NULL, then ASSERT().
328 If DevicePath2 is NULL, then ASSERT().
330 @param[in] DevicePath1 A pointer to a device path.
331 @param[in] DevicePath2 A pointer to a device path.
333 @retval TRUE Two device paths are identical , or DevicePath2 is
334 DevicePath1's child device path.
335 @retval FALSE Two device paths are not identical, and DevicePath2
336 is not DevicePath1's child device path.
341 IN CONST EFI_DEVICE_PATH_PROTOCOL
*DevicePath1
,
342 IN CONST EFI_DEVICE_PATH_PROTOCOL
*DevicePath2
346 EFI_STRING DevicePathStr1
;
347 EFI_STRING DevicePathStr2
;
350 EFI_DEVICE_PATH_TO_TEXT_PROTOCOL
*DevicePathText
;
351 BOOLEAN DevicePathEqual
;
353 ASSERT (DevicePath1
!= NULL
);
354 ASSERT (DevicePath2
!= NULL
);
356 DevicePathEqual
= FALSE
;
357 DevicePathText
= NULL
;
358 Status
= gBS
->LocateProtocol (
359 &gEfiDevicePathToTextProtocolGuid
,
361 (VOID
**) &DevicePathText
363 ASSERT (Status
== EFI_SUCCESS
);
366 // Get first device path string.
368 DevicePathStr1
= DevicePathText
->ConvertDevicePathToText (DevicePath1
, TRUE
, TRUE
);
369 ConvertDPStr (DevicePathStr1
);
371 // Get second device path string.
373 DevicePathStr2
= DevicePathText
->ConvertDevicePathToText (DevicePath2
, TRUE
, TRUE
);
374 ConvertDPStr (DevicePathStr2
);
377 // Compare device path string.
379 StrLen1
= StrSize (DevicePathStr1
);
380 StrLen2
= StrSize (DevicePathStr2
);
381 if (StrLen1
> StrLen2
) {
382 DevicePathEqual
= FALSE
;
386 if (CompareMem (DevicePathStr1
, DevicePathStr2
, StrLen1
) == 0) {
387 DevicePathEqual
= TRUE
;
391 FreePool (DevicePathStr1
);
392 FreePool (DevicePathStr2
);
393 return DevicePathEqual
;
398 Check whether the image pointed to by DevicePath is in the device path list
399 specified by AccessType.
401 @param[in] DevicePath Points to device path.
402 @param[in] AccessType The type of user access control.
404 @retval TURE The DevicePath is in the specified List.
405 @retval FALSE The DevicePath is not in the specified List.
410 IN CONST EFI_DEVICE_PATH_PROTOCOL
*DevicePath
,
415 EFI_USER_INFO_ACCESS_CONTROL
*Access
;
416 EFI_DEVICE_PATH_PROTOCOL
*Path
;
419 Status
= GetAccessControl (&Access
, AccessType
);
420 if (EFI_ERROR (Status
)) {
425 while (OffSet
< Access
->Size
- sizeof (EFI_USER_INFO_ACCESS_CONTROL
)) {
426 Path
= (EFI_DEVICE_PATH_PROTOCOL
*)((UINT8
*)(Access
+ 1) + OffSet
);
427 if (CheckDevicePath (Path
, DevicePath
)) {
429 // The device path is found in list.
434 OffSet
+= GetDevicePathSize (Path
);
443 Check whether the image pointed to by DevicePath is permitted to load.
445 @param[in] DevicePath Points to device path
447 @retval TURE The image pointed by DevicePath is permitted to load.
448 @retval FALSE The image pointed by DevicePath is forbidden to load.
453 IN CONST EFI_DEVICE_PATH_PROTOCOL
*DevicePath
456 if (IsDevicePathInList (DevicePath
, EFI_USER_INFO_ACCESS_PERMIT_LOAD
)) {
458 // This access control overrides any restrictions put in place by the
459 // EFI_USER_INFO_ACCESS_FORBID_LOAD record.
464 if (IsDevicePathInList (DevicePath
, EFI_USER_INFO_ACCESS_FORBID_LOAD
)) {
466 // The device path is found in the forbidden list.
476 Check the image pointed by DevicePath is a boot option or not.
478 @param[in] DevicePath Points to device path.
480 @retval TURE The image pointed by DevicePath is a boot option.
481 @retval FALSE The image pointed by DevicePath is not a boot option.
486 IN CONST EFI_DEVICE_PATH_PROTOCOL
*DevicePath
490 UINT16
*BootOrderList
;
491 UINTN BootOrderListSize
;
496 EFI_DEVICE_PATH_PROTOCOL
*OptionDevicePath
;
501 BootOrderListSize
= 0;
502 BootOrderList
= NULL
;
503 Status
= gRT
->GetVariable (
505 &gEfiGlobalVariableGuid
,
510 if (Status
== EFI_BUFFER_TOO_SMALL
) {
511 BootOrderList
= AllocateZeroPool (BootOrderListSize
);
512 ASSERT (BootOrderList
!= NULL
);
513 Status
= gRT
->GetVariable (
515 &gEfiGlobalVariableGuid
,
522 if (EFI_ERROR (Status
)) {
530 for (Index
= 0; Index
< BootOrderListSize
/ sizeof (UINT16
); Index
++) {
532 // Try to find the DevicePath in BootOption
534 UnicodeSPrint (StrTemp
, sizeof (StrTemp
), L
"Boot%04x", Index
);
535 GetEfiGlobalVariable2 (StrTemp
, &OptionBuffer
, NULL
);
536 if (OptionBuffer
== NULL
) {
541 // Check whether the image is forbidden.
544 OptionPtr
= OptionBuffer
;
548 OptionPtr
+= sizeof (UINT32
);
551 // Skip device path length.
553 OptionPtr
+= sizeof (UINT16
);
556 // Skip descript string
558 OptionPtr
+= StrSize ((UINT16
*) OptionPtr
);
561 // Now OptionPtr points to Device Path.
563 OptionDevicePath
= (EFI_DEVICE_PATH_PROTOCOL
*) OptionPtr
;
565 if (CheckDevicePath (DevicePath
, OptionDevicePath
)) {
566 FreePool (OptionBuffer
);
570 FreePool (OptionBuffer
);
574 if (BootOrderList
!= NULL
) {
575 FreePool (BootOrderList
);
583 Add the image info to a deferred image list.
585 @param[in] ImageDevicePath A pointer to the device path of a image.
586 @param[in] Image Points to the first byte of the image, or NULL if the
587 image is not available.
588 @param[in] ImageSize The size of the image, or 0 if the image is not available.
592 PutDefferedImageInfo (
593 IN CONST EFI_DEVICE_PATH_PROTOCOL
*ImageDevicePath
,
598 DEFERRED_IMAGE_INFO
*CurImageInfo
;
602 // Expand memory for the new deferred image.
604 if (mDeferredImage
.Count
== 0) {
605 mDeferredImage
.ImageInfo
= AllocatePool (sizeof (DEFERRED_IMAGE_INFO
));
606 ASSERT (mDeferredImage
.ImageInfo
!= NULL
);
608 CurImageInfo
= AllocatePool ((mDeferredImage
.Count
+ 1) * sizeof (DEFERRED_IMAGE_INFO
));
609 ASSERT (CurImageInfo
!= NULL
);
613 mDeferredImage
.ImageInfo
,
614 mDeferredImage
.Count
* sizeof (DEFERRED_IMAGE_INFO
)
616 FreePool (mDeferredImage
.ImageInfo
);
617 mDeferredImage
.ImageInfo
= CurImageInfo
;
619 mDeferredImage
.Count
++;
622 // Save the deferred image information.
624 CurImageInfo
= &mDeferredImage
.ImageInfo
[mDeferredImage
.Count
- 1];
625 PathSize
= GetDevicePathSize (ImageDevicePath
);
626 CurImageInfo
->ImageDevicePath
= AllocateZeroPool (PathSize
);
627 ASSERT (CurImageInfo
->ImageDevicePath
!= NULL
);
628 CopyMem (CurImageInfo
->ImageDevicePath
, ImageDevicePath
, PathSize
);
630 CurImageInfo
->Image
= Image
;
631 CurImageInfo
->ImageSize
= ImageSize
;
632 CurImageInfo
->BootOption
= IsBootOption (ImageDevicePath
);
637 Returns information about a deferred image.
639 This function returns information about a single deferred image. The deferred images are
640 numbered consecutively, starting with 0. If there is no image which corresponds to
641 ImageIndex, then EFI_NOT_FOUND is returned. All deferred images may be returned by
642 iteratively calling this function until EFI_NOT_FOUND is returned.
643 Image may be NULL and ImageSize set to 0 if the decision to defer execution was made
644 because of the location of the executable image, rather than its actual contents.
646 @param[in] This Points to this instance of the EFI_DEFERRED_IMAGE_LOAD_PROTOCOL.
647 @param[in] ImageIndex Zero-based index of the deferred index.
648 @param[out] ImageDevicePath On return, points to a pointer to the device path of the image.
649 The device path should not be freed by the caller.
650 @param[out] Image On return, points to the first byte of the image or NULL if the
651 image is not available. The image should not be freed by the caller
652 unless LoadImage() has been successfully called.
653 @param[out] ImageSize On return, the size of the image, or 0 if the image is not available.
654 @param[out] BootOption On return, points to TRUE if the image was intended as a boot option
655 or FALSE if it was not intended as a boot option.
657 @retval EFI_SUCCESS Image information returned successfully.
658 @retval EFI_NOT_FOUND ImageIndex does not refer to a valid image.
659 @retval EFI_INVALID_PARAMETER ImageDevicePath is NULL or Image is NULL or ImageSize is NULL or
665 GetDefferedImageInfo (
666 IN EFI_DEFERRED_IMAGE_LOAD_PROTOCOL
*This
,
668 OUT EFI_DEVICE_PATH_PROTOCOL
**ImageDevicePath
,
670 OUT UINTN
*ImageSize
,
671 OUT BOOLEAN
*BootOption
674 DEFERRED_IMAGE_INFO
*ReqImageInfo
;
677 // Check the parameter.
680 if ((This
== NULL
) || (ImageSize
== NULL
) || (Image
== NULL
)) {
681 return EFI_INVALID_PARAMETER
;
684 if ((ImageDevicePath
== NULL
) || (BootOption
== NULL
)) {
685 return EFI_INVALID_PARAMETER
;
688 if (ImageIndex
>= mDeferredImage
.Count
) {
689 return EFI_NOT_FOUND
;
693 // Get the request deferred image.
695 ReqImageInfo
= &mDeferredImage
.ImageInfo
[ImageIndex
];
697 *ImageDevicePath
= ReqImageInfo
->ImageDevicePath
;
698 *Image
= ReqImageInfo
->Image
;
699 *ImageSize
= ReqImageInfo
->ImageSize
;
700 *BootOption
= ReqImageInfo
->BootOption
;
707 Provides the service of deferring image load based on platform policy control,
708 and installs Deferred Image Load Protocol.
710 @param[in] AuthenticationStatus This is the authentication status returned from the
711 security measurement services for the input file.
712 @param[in] File This is a pointer to the device path of the file that
713 is being dispatched. This will optionally be used for
715 @param[in] FileBuffer File buffer matches the input file device path.
716 @param[in] FileSize Size of File buffer matches the input file device path.
718 @retval EFI_SUCCESS The file specified by File did authenticate, and the
719 platform policy dictates that the DXE Core may use File.
720 @retval EFI_INVALID_PARAMETER File is NULL.
721 @retval EFI_SECURITY_VIOLATION The file specified by File did not authenticate, and
722 the platform policy dictates that File should be placed
723 in the untrusted state. A file may be promoted from
724 the untrusted to the trusted state at a future time
725 with a call to the Trust() DXE Service.
726 @retval EFI_ACCESS_DENIED The file specified by File did not authenticate, and
727 the platform policy dictates that File should not be
728 used for any purpose.
733 DxeDeferImageLoadHandler (
734 IN UINT32 AuthenticationStatus
,
735 IN CONST EFI_DEVICE_PATH_PROTOCOL
*File
,
742 EFI_USER_PROFILE_HANDLE CurrentUser
;
747 return EFI_INVALID_PARAMETER
;
751 // Check whether user has a logon.
754 if (mUserManager
!= NULL
) {
755 mUserManager
->Current (mUserManager
, &CurrentUser
);
756 if (CurrentUser
!= NULL
) {
758 // The user is logon; verify the FilePath by current user access policy.
760 if (!VerifyDevicePath (File
)) {
761 DEBUG ((EFI_D_ERROR
, "[Security] The image is forbidden to load!\n"));
762 return EFI_ACCESS_DENIED
;
769 // Still no user logon.
770 // Check the file type and get policy setting.
772 FileType
= GetFileType (File
);
773 Policy
= PcdGet32 (PcdDeferImageLoadPolicy
);
774 if ((Policy
& FileType
) == FileType
) {
776 // This file type is secure to load.
781 DEBUG ((EFI_D_ERROR
, "[Security] No user identified, the image is deferred to load!\n"));
782 PutDefferedImageInfo (File
, NULL
, 0);
785 // Install the Deferred Image Load Protocol onto a new handle.
787 if (!mIsProtocolInstalled
) {
788 Status
= gBS
->InstallMultipleProtocolInterfaces (
789 &mDeferredImageHandle
,
790 &gEfiDeferredImageLoadProtocolGuid
,
794 ASSERT_EFI_ERROR (Status
);
795 mIsProtocolInstalled
= TRUE
;
798 return EFI_ACCESS_DENIED
;
802 Locate user manager protocol when user manager is installed.
804 @param[in] Event The Event that is being processed, not used.
805 @param[in] Context Event Context, not used.
810 FindUserManagerProtocol (
815 gBS
->LocateProtocol (
816 &gEfiUserManagerProtocolGuid
,
818 (VOID
**) &mUserManager
825 Register security handler for deferred image load.
827 @param[in] ImageHandle ImageHandle of the loaded driver.
828 @param[in] SystemTable Pointer to the EFI System Table.
830 @retval EFI_SUCCESS The handlers were registered successfully.
834 DxeDeferImageLoadLibConstructor (
835 IN EFI_HANDLE ImageHandle
,
836 IN EFI_SYSTEM_TABLE
*SystemTable
842 // Register user manager notification function.
844 EfiCreateProtocolNotifyEvent (
845 &gEfiUserManagerProtocolGuid
,
847 FindUserManagerProtocol
,
852 return RegisterSecurityHandler (
853 DxeDeferImageLoadHandler
,
854 EFI_AUTH_OPERATION_DEFER_IMAGE_LOAD