| /** @file | |
| Implement defer image load services for user identification in UEFI2.2. | |
| Copyright (c) 2009 - 2014, Intel Corporation. All rights reserved.<BR> | |
| This program and the accompanying materials | |
| are licensed and made available under the terms and conditions of the BSD License | |
| which accompanies this distribution. The full text of the license may be found at | |
| http://opensource.org/licenses/bsd-license.php | |
| THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, | |
| WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. | |
| **/ | |
| #include "DxeDeferImageLoadLib.h" | |
| // | |
| // Handle for the Deferred Image Load Protocol instance produced by this driver. | |
| // | |
| EFI_HANDLE mDeferredImageHandle = NULL; | |
| BOOLEAN mIsProtocolInstalled = FALSE; | |
| EFI_USER_MANAGER_PROTOCOL *mUserManager = NULL; | |
| DEFERRED_IMAGE_TABLE mDeferredImage = { | |
| 0, // Deferred image count | |
| NULL // The deferred image info | |
| }; | |
| EFI_DEFERRED_IMAGE_LOAD_PROTOCOL gDeferredImageLoad = { | |
| GetDefferedImageInfo | |
| }; | |
| /** | |
| Get the image type. | |
| @param[in] File This is a pointer to the device path of the file | |
| that is being dispatched. | |
| @return UINT32 Image Type | |
| **/ | |
| UINT32 | |
| GetFileType ( | |
| IN CONST EFI_DEVICE_PATH_PROTOCOL *File | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| EFI_HANDLE DeviceHandle; | |
| EFI_DEVICE_PATH_PROTOCOL *TempDevicePath; | |
| EFI_BLOCK_IO_PROTOCOL *BlockIo; | |
| // | |
| // First check to see if File is from a Firmware Volume | |
| // | |
| DeviceHandle = NULL; | |
| TempDevicePath = (EFI_DEVICE_PATH_PROTOCOL *)File; | |
| Status = gBS->LocateDevicePath ( | |
| &gEfiFirmwareVolume2ProtocolGuid, | |
| &TempDevicePath, | |
| &DeviceHandle | |
| ); | |
| if (!EFI_ERROR (Status)) { | |
| Status = gBS->OpenProtocol ( | |
| DeviceHandle, | |
| &gEfiFirmwareVolume2ProtocolGuid, | |
| NULL, | |
| NULL, | |
| NULL, | |
| EFI_OPEN_PROTOCOL_TEST_PROTOCOL | |
| ); | |
| if (!EFI_ERROR (Status)) { | |
| return IMAGE_FROM_FV; | |
| } | |
| } | |
| // | |
| // Next check to see if File is from a Block I/O device | |
| // | |
| DeviceHandle = NULL; | |
| TempDevicePath = (EFI_DEVICE_PATH_PROTOCOL *)File; | |
| Status = gBS->LocateDevicePath ( | |
| &gEfiBlockIoProtocolGuid, | |
| &TempDevicePath, | |
| &DeviceHandle | |
| ); | |
| if (!EFI_ERROR (Status)) { | |
| BlockIo = NULL; | |
| Status = gBS->OpenProtocol ( | |
| DeviceHandle, | |
| &gEfiBlockIoProtocolGuid, | |
| (VOID **) &BlockIo, | |
| NULL, | |
| NULL, | |
| EFI_OPEN_PROTOCOL_GET_PROTOCOL | |
| ); | |
| if (!EFI_ERROR (Status) && BlockIo != NULL) { | |
| if (BlockIo->Media != NULL) { | |
| if (BlockIo->Media->RemovableMedia) { | |
| // | |
| // Block I/O is present and specifies the media is removable | |
| // | |
| return IMAGE_FROM_REMOVABLE_MEDIA; | |
| } else { | |
| // | |
| // Block I/O is present and specifies the media is not removable | |
| // | |
| return IMAGE_FROM_FIXED_MEDIA; | |
| } | |
| } | |
| } | |
| } | |
| // | |
| // File is not in a Firmware Volume or on a Block I/O device, so check to see if | |
| // the device path supports the Simple File System Protocol. | |
| // | |
| DeviceHandle = NULL; | |
| TempDevicePath = (EFI_DEVICE_PATH_PROTOCOL *)File; | |
| Status = gBS->LocateDevicePath ( | |
| &gEfiSimpleFileSystemProtocolGuid, | |
| &TempDevicePath, | |
| &DeviceHandle | |
| ); | |
| if (!EFI_ERROR (Status)) { | |
| // | |
| // Simple File System is present without Block I/O, so assume media is fixed. | |
| // | |
| return IMAGE_FROM_FIXED_MEDIA; | |
| } | |
| // | |
| // File is not from an FV, Block I/O or Simple File System, so the only options | |
| // left are a PCI Option ROM and a Load File Protocol such as a PXE Boot from a NIC. | |
| // | |
| TempDevicePath = (EFI_DEVICE_PATH_PROTOCOL *)File; | |
| while (!IsDevicePathEndType (TempDevicePath)) { | |
| switch (DevicePathType (TempDevicePath)) { | |
| case MEDIA_DEVICE_PATH: | |
| if (DevicePathSubType (TempDevicePath) == MEDIA_RELATIVE_OFFSET_RANGE_DP) { | |
| return IMAGE_FROM_OPTION_ROM; | |
| } | |
| break; | |
| case MESSAGING_DEVICE_PATH: | |
| if (DevicePathSubType(TempDevicePath) == MSG_MAC_ADDR_DP) { | |
| return IMAGE_FROM_REMOVABLE_MEDIA; | |
| } | |
| break; | |
| default: | |
| break; | |
| } | |
| TempDevicePath = NextDevicePathNode (TempDevicePath); | |
| } | |
| return IMAGE_UNKNOWN; | |
| } | |
| /** | |
| Get current user's access right. | |
| @param[out] AccessControl Points to the user's access control data, the | |
| caller should free data buffer. | |
| @param[in] AccessType The type of user access control. | |
| @retval EFI_SUCCESS Get current user access control successfully | |
| @retval others Fail to get current user access control | |
| **/ | |
| EFI_STATUS | |
| GetAccessControl ( | |
| OUT EFI_USER_INFO_ACCESS_CONTROL **AccessControl, | |
| IN UINT32 AccessType | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| EFI_USER_INFO_HANDLE UserInfo; | |
| EFI_USER_INFO *Info; | |
| UINTN InfoSize; | |
| EFI_USER_INFO_ACCESS_CONTROL *Access; | |
| EFI_USER_PROFILE_HANDLE CurrentUser; | |
| UINTN CheckLen; | |
| EFI_USER_MANAGER_PROTOCOL *UserManager; | |
| CurrentUser = NULL; | |
| Status = gBS->LocateProtocol ( | |
| &gEfiUserManagerProtocolGuid, | |
| NULL, | |
| (VOID **) &UserManager | |
| ); | |
| if (EFI_ERROR (Status)) { | |
| return EFI_NOT_FOUND; | |
| } | |
| // | |
| // Get current user access information. | |
| // | |
| UserManager->Current (UserManager, &CurrentUser); | |
| UserInfo = NULL; | |
| Info = NULL; | |
| InfoSize = 0; | |
| while (TRUE) { | |
| // | |
| // Get next user information. | |
| // | |
| Status = UserManager->GetNextInfo (UserManager, CurrentUser, &UserInfo); | |
| if (EFI_ERROR (Status)) { | |
| return Status; | |
| } | |
| Status = UserManager->GetInfo ( | |
| UserManager, | |
| CurrentUser, | |
| UserInfo, | |
| Info, | |
| &InfoSize | |
| ); | |
| if (Status == EFI_BUFFER_TOO_SMALL) { | |
| if (Info != NULL) { | |
| FreePool (Info); | |
| } | |
| Info = AllocateZeroPool (InfoSize); | |
| ASSERT (Info != NULL); | |
| Status = UserManager->GetInfo ( | |
| UserManager, | |
| CurrentUser, | |
| UserInfo, | |
| Info, | |
| &InfoSize | |
| ); | |
| } | |
| if (EFI_ERROR (Status)) { | |
| break; | |
| } | |
| ASSERT (Info != NULL); | |
| if (Info->InfoType != EFI_USER_INFO_ACCESS_POLICY_RECORD) { | |
| continue; | |
| } | |
| // | |
| // Get specified access information. | |
| // | |
| CheckLen = 0; | |
| while (CheckLen < Info->InfoSize - sizeof (EFI_USER_INFO)) { | |
| Access = (EFI_USER_INFO_ACCESS_CONTROL *) ((UINT8 *) (Info + 1) + CheckLen); | |
| if (Access->Type == AccessType) { | |
| *AccessControl = AllocateZeroPool (Access->Size); | |
| ASSERT (*AccessControl != NULL); | |
| CopyMem (*AccessControl, Access, Access->Size); | |
| FreePool (Info); | |
| return EFI_SUCCESS; | |
| } | |
| CheckLen += Access->Size; | |
| } | |
| } | |
| if (Info != NULL) { | |
| FreePool (Info); | |
| } | |
| return EFI_NOT_FOUND; | |
| } | |
| /** | |
| Get file name from device path. | |
| The file name may contain one or more device path node. Save the file name in a | |
| buffer if file name is found. The caller is responsible to free the buffer. | |
| @param[in] DevicePath A pointer to a device path. | |
| @param[out] FileName The callee allocated buffer to save the file name if file name is found. | |
| @param[out] FileNameOffset The offset of file name in device path if file name is found. | |
| @retval UINTN The file name length. 0 means file name is not found. | |
| **/ | |
| UINTN | |
| GetFileName ( | |
| IN CONST EFI_DEVICE_PATH_PROTOCOL *DevicePath, | |
| OUT UINT8 **FileName, | |
| OUT UINTN *FileNameOffset | |
| ) | |
| { | |
| UINTN Length; | |
| EFI_DEVICE_PATH_PROTOCOL *TmpDevicePath; | |
| EFI_DEVICE_PATH_PROTOCOL *RootDevicePath; | |
| CHAR8 *NodeStr; | |
| UINTN NodeStrLength; | |
| CHAR16 LastNodeChar; | |
| CHAR16 FirstNodeChar; | |
| // | |
| // Get the length of DevicePath before file name. | |
| // | |
| Length = 0; | |
| RootDevicePath = (EFI_DEVICE_PATH_PROTOCOL *)DevicePath; | |
| while (!IsDevicePathEnd (RootDevicePath)) { | |
| if ((DevicePathType(RootDevicePath) == MEDIA_DEVICE_PATH) && (DevicePathSubType(RootDevicePath) == MEDIA_FILEPATH_DP)) { | |
| break; | |
| } | |
| Length += DevicePathNodeLength (RootDevicePath); | |
| RootDevicePath = NextDevicePathNode (RootDevicePath); | |
| } | |
| *FileNameOffset = Length; | |
| if (Length == 0) { | |
| return 0; | |
| } | |
| // | |
| // Get the file name length. | |
| // | |
| Length = 0; | |
| TmpDevicePath = RootDevicePath; | |
| while (!IsDevicePathEnd (TmpDevicePath)) { | |
| if ((DevicePathType(TmpDevicePath) != MEDIA_DEVICE_PATH) || (DevicePathSubType(TmpDevicePath) != MEDIA_FILEPATH_DP)) { | |
| break; | |
| } | |
| Length += DevicePathNodeLength (TmpDevicePath) - sizeof (EFI_DEVICE_PATH_PROTOCOL); | |
| TmpDevicePath = NextDevicePathNode (TmpDevicePath); | |
| } | |
| if (Length == 0) { | |
| return 0; | |
| } | |
| *FileName = AllocateZeroPool (Length); | |
| ASSERT (*FileName != NULL); | |
| // | |
| // Copy the file name to the buffer. | |
| // | |
| Length = 0; | |
| LastNodeChar = '\\'; | |
| TmpDevicePath = RootDevicePath; | |
| while (!IsDevicePathEnd (TmpDevicePath)) { | |
| if ((DevicePathType(TmpDevicePath) != MEDIA_DEVICE_PATH) || (DevicePathSubType(TmpDevicePath) != MEDIA_FILEPATH_DP)) { | |
| break; | |
| } | |
| FirstNodeChar = (CHAR16) ReadUnaligned16 ((UINT16 *)((UINT8 *)TmpDevicePath + sizeof (EFI_DEVICE_PATH_PROTOCOL))); | |
| NodeStr = (CHAR8 *)TmpDevicePath + sizeof (EFI_DEVICE_PATH_PROTOCOL); | |
| NodeStrLength = DevicePathNodeLength (TmpDevicePath) - sizeof (EFI_DEVICE_PATH_PROTOCOL) - sizeof(CHAR16); | |
| if ((FirstNodeChar == '\\') && (LastNodeChar == '\\')) { | |
| // | |
| // Skip separator "\" when there are two separators. | |
| // | |
| NodeStr += sizeof (CHAR16); | |
| NodeStrLength -= sizeof (CHAR16); | |
| } else if ((FirstNodeChar != '\\') && (LastNodeChar != '\\')) { | |
| // | |
| // Add separator "\" when there is no separator. | |
| // | |
| WriteUnaligned16 ((UINT16 *)(*FileName + Length), '\\'); | |
| Length += sizeof (CHAR16); | |
| } | |
| CopyMem (*FileName + Length, NodeStr, NodeStrLength); | |
| Length += NodeStrLength; | |
| LastNodeChar = (CHAR16) ReadUnaligned16 ((UINT16 *) (NodeStr + NodeStrLength - sizeof(CHAR16))); | |
| TmpDevicePath = NextDevicePathNode (TmpDevicePath); | |
| } | |
| return Length; | |
| } | |
| /** | |
| Check whether the DevicePath2 is identical with DevicePath1, or identical with | |
| DevicePath1's child device path. | |
| If DevicePath2 is identical with DevicePath1, or with DevicePath1's child device | |
| path, then TRUE returned. Otherwise, FALSE is returned. | |
| If DevicePath1 is NULL, then ASSERT(). | |
| If DevicePath2 is NULL, then ASSERT(). | |
| @param[in] DevicePath1 A pointer to a device path. | |
| @param[in] DevicePath2 A pointer to a device path. | |
| @retval TRUE Two device paths are identical , or DevicePath2 is | |
| DevicePath1's child device path. | |
| @retval FALSE Two device paths are not identical, and DevicePath2 | |
| is not DevicePath1's child device path. | |
| **/ | |
| BOOLEAN | |
| CheckDevicePath ( | |
| IN CONST EFI_DEVICE_PATH_PROTOCOL *DevicePath1, | |
| IN CONST EFI_DEVICE_PATH_PROTOCOL *DevicePath2 | |
| ) | |
| { | |
| UINTN DevicePathSize; | |
| UINTN FileNameSize1; | |
| UINTN FileNameSize2; | |
| UINT8 *FileName1; | |
| UINT8 *FileName2; | |
| UINTN FileNameOffset1; | |
| UINTN FileNameOffset2; | |
| BOOLEAN DevicePathEqual; | |
| FileName1 = NULL; | |
| FileName2 = NULL; | |
| DevicePathEqual = TRUE; | |
| ASSERT (DevicePath1 != NULL); | |
| ASSERT (DevicePath2 != NULL); | |
| if (IsDevicePathEnd (DevicePath1)) { | |
| return FALSE; | |
| } | |
| // | |
| // The file name may contain one or more device path node. | |
| // To compare the file name, copy file name to a buffer and compare the buffer. | |
| // | |
| FileNameSize1 = GetFileName (DevicePath1, &FileName1, &FileNameOffset1); | |
| if (FileNameSize1 != 0) { | |
| FileNameSize2 = GetFileName (DevicePath2, &FileName2, &FileNameOffset2); | |
| if (FileNameOffset1 != FileNameOffset2) { | |
| DevicePathEqual = FALSE; | |
| goto Done; | |
| } | |
| if (CompareMem (DevicePath1, DevicePath2, FileNameOffset1) != 0) { | |
| DevicePathEqual = FALSE; | |
| goto Done; | |
| } | |
| if (FileNameSize1 > FileNameSize2) { | |
| DevicePathEqual = FALSE; | |
| goto Done; | |
| } | |
| if (CompareMem (FileName1, FileName2, FileNameSize1) != 0) { | |
| DevicePathEqual = FALSE; | |
| goto Done; | |
| } | |
| DevicePathEqual = TRUE; | |
| goto Done; | |
| } | |
| DevicePathSize = GetDevicePathSize (DevicePath1); | |
| if (DevicePathSize > GetDevicePathSize (DevicePath2)) { | |
| return FALSE; | |
| } | |
| // | |
| // Exclude the end of device path node. | |
| // | |
| DevicePathSize -= sizeof (EFI_DEVICE_PATH_PROTOCOL); | |
| if (CompareMem (DevicePath1, DevicePath2, DevicePathSize) != 0) { | |
| DevicePathEqual = FALSE; | |
| } | |
| Done: | |
| if (FileName1 != NULL) { | |
| FreePool (FileName1); | |
| } | |
| if (FileName2 != NULL) { | |
| FreePool (FileName2); | |
| } | |
| return DevicePathEqual; | |
| } | |
| /** | |
| Check whether the image pointed to by DevicePath is in the device path list | |
| specified by AccessType. | |
| @param[in] DevicePath Points to device path. | |
| @param[in] AccessType The type of user access control. | |
| @retval TRUE The DevicePath is in the specified List. | |
| @retval FALSE The DevicePath is not in the specified List. | |
| **/ | |
| BOOLEAN | |
| IsDevicePathInList ( | |
| IN CONST EFI_DEVICE_PATH_PROTOCOL *DevicePath, | |
| IN UINT32 AccessType | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| EFI_USER_INFO_ACCESS_CONTROL *Access; | |
| EFI_DEVICE_PATH_PROTOCOL *Path; | |
| UINTN OffSet; | |
| Status = GetAccessControl (&Access, AccessType); | |
| if (EFI_ERROR (Status)) { | |
| return FALSE; | |
| } | |
| OffSet = 0; | |
| while (OffSet < Access->Size - sizeof (EFI_USER_INFO_ACCESS_CONTROL)) { | |
| Path = (EFI_DEVICE_PATH_PROTOCOL*)((UINT8*)(Access + 1) + OffSet); | |
| if (CheckDevicePath (Path, DevicePath)) { | |
| // | |
| // The device path is found in list. | |
| // | |
| FreePool (Access); | |
| return TRUE; | |
| } | |
| OffSet += GetDevicePathSize (Path); | |
| } | |
| FreePool (Access); | |
| return FALSE; | |
| } | |
| /** | |
| Check whether the image pointed to by DevicePath is permitted to load. | |
| @param[in] DevicePath Points to device path | |
| @retval TRUE The image pointed by DevicePath is permitted to load. | |
| @retval FALSE The image pointed by DevicePath is forbidden to load. | |
| **/ | |
| BOOLEAN | |
| VerifyDevicePath ( | |
| IN CONST EFI_DEVICE_PATH_PROTOCOL *DevicePath | |
| ) | |
| { | |
| if (IsDevicePathInList (DevicePath, EFI_USER_INFO_ACCESS_PERMIT_LOAD)) { | |
| // | |
| // This access control overrides any restrictions put in place by the | |
| // EFI_USER_INFO_ACCESS_FORBID_LOAD record. | |
| // | |
| return TRUE; | |
| } | |
| if (IsDevicePathInList (DevicePath, EFI_USER_INFO_ACCESS_FORBID_LOAD)) { | |
| // | |
| // The device path is found in the forbidden list. | |
| // | |
| return FALSE; | |
| } | |
| return TRUE; | |
| } | |
| /** | |
| Check the image pointed by DevicePath is a boot option or not. | |
| @param[in] DevicePath Points to device path. | |
| @retval TRUE The image pointed by DevicePath is a boot option. | |
| @retval FALSE The image pointed by DevicePath is not a boot option. | |
| **/ | |
| BOOLEAN | |
| IsBootOption ( | |
| IN CONST EFI_DEVICE_PATH_PROTOCOL *DevicePath | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| UINT16 *BootOrderList; | |
| UINTN BootOrderListSize; | |
| UINTN Index; | |
| CHAR16 StrTemp[20]; | |
| UINT8 *OptionBuffer; | |
| UINT8 *OptionPtr; | |
| EFI_DEVICE_PATH_PROTOCOL *OptionDevicePath; | |
| // | |
| // Get BootOrder | |
| // | |
| BootOrderListSize = 0; | |
| BootOrderList = NULL; | |
| Status = gRT->GetVariable ( | |
| L"BootOrder", | |
| &gEfiGlobalVariableGuid, | |
| NULL, | |
| &BootOrderListSize, | |
| NULL | |
| ); | |
| if (Status == EFI_BUFFER_TOO_SMALL) { | |
| BootOrderList = AllocateZeroPool (BootOrderListSize); | |
| ASSERT (BootOrderList != NULL); | |
| Status = gRT->GetVariable ( | |
| L"BootOrder", | |
| &gEfiGlobalVariableGuid, | |
| NULL, | |
| &BootOrderListSize, | |
| BootOrderList | |
| ); | |
| } | |
| if (EFI_ERROR (Status)) { | |
| // | |
| // No Boot option | |
| // | |
| return FALSE; | |
| } | |
| OptionBuffer = NULL; | |
| for (Index = 0; Index < BootOrderListSize / sizeof (UINT16); Index++) { | |
| // | |
| // Try to find the DevicePath in BootOption | |
| // | |
| UnicodeSPrint (StrTemp, sizeof (StrTemp), L"Boot%04x", Index); | |
| GetEfiGlobalVariable2 (StrTemp, (VOID**)&OptionBuffer, NULL); | |
| if (OptionBuffer == NULL) { | |
| continue; | |
| } | |
| // | |
| // Check whether the image is forbidden. | |
| // | |
| OptionPtr = OptionBuffer; | |
| // | |
| // Skip attribute. | |
| // | |
| OptionPtr += sizeof (UINT32); | |
| // | |
| // Skip device path length. | |
| // | |
| OptionPtr += sizeof (UINT16); | |
| // | |
| // Skip descript string | |
| // | |
| OptionPtr += StrSize ((UINT16 *) OptionPtr); | |
| // | |
| // Now OptionPtr points to Device Path. | |
| // | |
| OptionDevicePath = (EFI_DEVICE_PATH_PROTOCOL *) OptionPtr; | |
| if (CheckDevicePath (DevicePath, OptionDevicePath)) { | |
| FreePool (OptionBuffer); | |
| OptionBuffer = NULL; | |
| return TRUE; | |
| } | |
| FreePool (OptionBuffer); | |
| OptionBuffer = NULL; | |
| } | |
| if (BootOrderList != NULL) { | |
| FreePool (BootOrderList); | |
| } | |
| return FALSE; | |
| } | |
| /** | |
| Add the image info to a deferred image list. | |
| @param[in] ImageDevicePath A pointer to the device path of a image. | |
| @param[in] Image Points to the first byte of the image, or NULL if the | |
| image is not available. | |
| @param[in] ImageSize The size of the image, or 0 if the image is not available. | |
| **/ | |
| VOID | |
| PutDefferedImageInfo ( | |
| IN CONST EFI_DEVICE_PATH_PROTOCOL *ImageDevicePath, | |
| IN VOID *Image, | |
| IN UINTN ImageSize | |
| ) | |
| { | |
| DEFERRED_IMAGE_INFO *CurImageInfo; | |
| UINTN PathSize; | |
| // | |
| // Expand memory for the new deferred image. | |
| // | |
| if (mDeferredImage.Count == 0) { | |
| mDeferredImage.ImageInfo = AllocatePool (sizeof (DEFERRED_IMAGE_INFO)); | |
| ASSERT (mDeferredImage.ImageInfo != NULL); | |
| } else { | |
| CurImageInfo = AllocatePool ((mDeferredImage.Count + 1) * sizeof (DEFERRED_IMAGE_INFO)); | |
| ASSERT (CurImageInfo != NULL); | |
| CopyMem ( | |
| CurImageInfo, | |
| mDeferredImage.ImageInfo, | |
| mDeferredImage.Count * sizeof (DEFERRED_IMAGE_INFO) | |
| ); | |
| FreePool (mDeferredImage.ImageInfo); | |
| mDeferredImage.ImageInfo = CurImageInfo; | |
| } | |
| mDeferredImage.Count++; | |
| // | |
| // Save the deferred image information. | |
| // | |
| CurImageInfo = &mDeferredImage.ImageInfo[mDeferredImage.Count - 1]; | |
| PathSize = GetDevicePathSize (ImageDevicePath); | |
| CurImageInfo->ImageDevicePath = AllocateZeroPool (PathSize); | |
| ASSERT (CurImageInfo->ImageDevicePath != NULL); | |
| CopyMem (CurImageInfo->ImageDevicePath, ImageDevicePath, PathSize); | |
| CurImageInfo->Image = Image; | |
| CurImageInfo->ImageSize = ImageSize; | |
| CurImageInfo->BootOption = IsBootOption (ImageDevicePath); | |
| } | |
| /** | |
| Returns information about a deferred image. | |
| This function returns information about a single deferred image. The deferred images are | |
| numbered consecutively, starting with 0. If there is no image which corresponds to | |
| ImageIndex, then EFI_NOT_FOUND is returned. All deferred images may be returned by | |
| iteratively calling this function until EFI_NOT_FOUND is returned. | |
| Image may be NULL and ImageSize set to 0 if the decision to defer execution was made | |
| because of the location of the executable image, rather than its actual contents. | |
| @param[in] This Points to this instance of the EFI_DEFERRED_IMAGE_LOAD_PROTOCOL. | |
| @param[in] ImageIndex Zero-based index of the deferred index. | |
| @param[out] ImageDevicePath On return, points to a pointer to the device path of the image. | |
| The device path should not be freed by the caller. | |
| @param[out] Image On return, points to the first byte of the image or NULL if the | |
| image is not available. The image should not be freed by the caller | |
| unless LoadImage() has been successfully called. | |
| @param[out] ImageSize On return, the size of the image, or 0 if the image is not available. | |
| @param[out] BootOption On return, points to TRUE if the image was intended as a boot option | |
| or FALSE if it was not intended as a boot option. | |
| @retval EFI_SUCCESS Image information returned successfully. | |
| @retval EFI_NOT_FOUND ImageIndex does not refer to a valid image. | |
| @retval EFI_INVALID_PARAMETER ImageDevicePath is NULL or Image is NULL or ImageSize is NULL or | |
| BootOption is NULL. | |
| **/ | |
| EFI_STATUS | |
| 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 | |
| ) | |
| { | |
| DEFERRED_IMAGE_INFO *ReqImageInfo; | |
| // | |
| // Check the parameter. | |
| // | |
| if ((This == NULL) || (ImageSize == NULL) || (Image == NULL)) { | |
| return EFI_INVALID_PARAMETER; | |
| } | |
| if ((ImageDevicePath == NULL) || (BootOption == NULL)) { | |
| return EFI_INVALID_PARAMETER; | |
| } | |
| if (ImageIndex >= mDeferredImage.Count) { | |
| return EFI_NOT_FOUND; | |
| } | |
| // | |
| // Get the request deferred image. | |
| // | |
| ReqImageInfo = &mDeferredImage.ImageInfo[ImageIndex]; | |
| *ImageDevicePath = ReqImageInfo->ImageDevicePath; | |
| *Image = ReqImageInfo->Image; | |
| *ImageSize = ReqImageInfo->ImageSize; | |
| *BootOption = ReqImageInfo->BootOption; | |
| return EFI_SUCCESS; | |
| } | |
| /** | |
| Provides the service of deferring image load based on platform policy control, | |
| and installs Deferred Image Load Protocol. | |
| @param[in] AuthenticationStatus This is the authentication status returned from the | |
| security measurement services for the input file. | |
| @param[in] File This is a pointer to the device path of the file that | |
| is being dispatched. This will optionally be used for | |
| logging. | |
| @param[in] FileBuffer File buffer matches the input file device path. | |
| @param[in] FileSize Size of File buffer matches the input file device path. | |
| @param[in] BootPolicy A boot policy that was used to call LoadImage() UEFI service. | |
| @retval EFI_SUCCESS FileBuffer is NULL and current user has permission to start | |
| UEFI device drivers on the device path specified by DevicePath. | |
| @retval EFI_SUCCESS The file specified by DevicePath and non-NULL | |
| FileBuffer did authenticate, and the platform policy dictates | |
| that the DXE Foundation may use the file. | |
| @retval EFI_SECURITY_VIOLATION FileBuffer is NULL and the user has no | |
| permission to start UEFI device drivers on the device path specified | |
| by DevicePath. | |
| @retval EFI_SECURITY_VIOLATION FileBuffer is not NULL and the user has no permission to load | |
| drivers from the device path specified by DevicePath. The | |
| image has been added into the list of the deferred images. | |
| @retval EFI_ACCESS_DENIED The file specified by File and FileBuffer did not | |
| authenticate, and the platform policy dictates that the DXE | |
| Foundation many not use File. | |
| **/ | |
| EFI_STATUS | |
| EFIAPI | |
| DxeDeferImageLoadHandler ( | |
| IN UINT32 AuthenticationStatus, | |
| IN CONST EFI_DEVICE_PATH_PROTOCOL *File, | |
| IN VOID *FileBuffer, | |
| IN UINTN FileSize, | |
| IN BOOLEAN BootPolicy | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| EFI_USER_PROFILE_HANDLE CurrentUser; | |
| UINT32 Policy; | |
| UINT32 FileType; | |
| // | |
| // Ignore if File is NULL. | |
| // | |
| if (File == NULL) { | |
| return EFI_SUCCESS; | |
| } | |
| // | |
| // Check whether user has a logon. | |
| // | |
| CurrentUser = NULL; | |
| if (mUserManager != NULL) { | |
| mUserManager->Current (mUserManager, &CurrentUser); | |
| if (CurrentUser != NULL) { | |
| // | |
| // The user is logon; verify the FilePath by current user access policy. | |
| // | |
| if (!VerifyDevicePath (File)) { | |
| DEBUG ((EFI_D_ERROR, "[Security] The image is forbidden to load!\n")); | |
| return EFI_SECURITY_VIOLATION; | |
| } | |
| return EFI_SUCCESS; | |
| } | |
| } | |
| // | |
| // Still no user logon. | |
| // Check the file type and get policy setting. | |
| // | |
| FileType = GetFileType (File); | |
| Policy = PcdGet32 (PcdDeferImageLoadPolicy); | |
| if ((Policy & FileType) == FileType) { | |
| // | |
| // This file type is secure to load. | |
| // | |
| return EFI_SUCCESS; | |
| } | |
| DEBUG ((EFI_D_INFO, "[Security] No user identified, the image is deferred to load!\n")); | |
| PutDefferedImageInfo (File, FileBuffer, FileSize); | |
| // | |
| // Install the Deferred Image Load Protocol onto a new handle. | |
| // | |
| if (!mIsProtocolInstalled) { | |
| Status = gBS->InstallMultipleProtocolInterfaces ( | |
| &mDeferredImageHandle, | |
| &gEfiDeferredImageLoadProtocolGuid, | |
| &gDeferredImageLoad, | |
| NULL | |
| ); | |
| ASSERT_EFI_ERROR (Status); | |
| mIsProtocolInstalled = TRUE; | |
| } | |
| return EFI_ACCESS_DENIED; | |
| } | |
| /** | |
| Locate user manager protocol when user manager is installed. | |
| @param[in] Event The Event that is being processed, not used. | |
| @param[in] Context Event Context, not used. | |
| **/ | |
| VOID | |
| EFIAPI | |
| FindUserManagerProtocol ( | |
| IN EFI_EVENT Event, | |
| IN VOID* Context | |
| ) | |
| { | |
| gBS->LocateProtocol ( | |
| &gEfiUserManagerProtocolGuid, | |
| NULL, | |
| (VOID **) &mUserManager | |
| ); | |
| } | |
| /** | |
| Register security handler for deferred image load. | |
| @param[in] ImageHandle ImageHandle of the loaded driver. | |
| @param[in] SystemTable Pointer to the EFI System Table. | |
| @retval EFI_SUCCESS The handlers were registered successfully. | |
| **/ | |
| EFI_STATUS | |
| EFIAPI | |
| DxeDeferImageLoadLibConstructor ( | |
| IN EFI_HANDLE ImageHandle, | |
| IN EFI_SYSTEM_TABLE *SystemTable | |
| ) | |
| { | |
| VOID *Registration; | |
| // | |
| // Register user manager notification function. | |
| // | |
| EfiCreateProtocolNotifyEvent ( | |
| &gEfiUserManagerProtocolGuid, | |
| TPL_CALLBACK, | |
| FindUserManagerProtocol, | |
| NULL, | |
| &Registration | |
| ); | |
| return RegisterSecurity2Handler ( | |
| DxeDeferImageLoadHandler, | |
| EFI_AUTH_OPERATION_DEFER_IMAGE_LOAD | |
| ); | |
| } | |