From 0c18794ea4289f03fefc7117b56740414cc0536c Mon Sep 17 00:00:00 2001 From: gdong1 Date: Fri, 2 Sep 2011 07:49:32 +0000 Subject: Add security package to repository. git-svn-id: https://edk2.svn.sourceforge.net/svnroot/edk2/trunk/edk2@12261 6f19259b-4bc3-4df7-8a09-765794883524 --- .../DxeDeferImageLoadLib/DxeDeferImageLoadLib.c | 858 ++++++++++++ .../DxeDeferImageLoadLib/DxeDeferImageLoadLib.h | 106 ++ .../DxeDeferImageLoadLib/DxeDeferImageLoadLib.inf | 62 + .../DxeImageVerificationLib.c | 1368 ++++++++++++++++++++ .../DxeImageVerificationLib.h | 201 +++ .../DxeImageVerificationLib.inf | 73 ++ .../DxeTpmMeasureBootLib/DxeTpmMeasureBootLib.c | 830 ++++++++++++ .../DxeTpmMeasureBootLib/DxeTpmMeasureBootLib.inf | 54 + .../PlatformSecureLibNull/PlatformSecureLibNull.c | 39 + .../PlatformSecureLibNull.inf | 33 + SecurityPkg/Library/TpmCommLib/CommonHeader.h | 29 + SecurityPkg/Library/TpmCommLib/TisPc.c | 180 +++ SecurityPkg/Library/TpmCommLib/TpmComm.c | 50 + SecurityPkg/Library/TpmCommLib/TpmCommLib.inf | 46 + 14 files changed, 3929 insertions(+) create mode 100644 SecurityPkg/Library/DxeDeferImageLoadLib/DxeDeferImageLoadLib.c create mode 100644 SecurityPkg/Library/DxeDeferImageLoadLib/DxeDeferImageLoadLib.h create mode 100644 SecurityPkg/Library/DxeDeferImageLoadLib/DxeDeferImageLoadLib.inf create mode 100644 SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.c create mode 100644 SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.h create mode 100644 SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.inf create mode 100644 SecurityPkg/Library/DxeTpmMeasureBootLib/DxeTpmMeasureBootLib.c create mode 100644 SecurityPkg/Library/DxeTpmMeasureBootLib/DxeTpmMeasureBootLib.inf create mode 100644 SecurityPkg/Library/PlatformSecureLibNull/PlatformSecureLibNull.c create mode 100644 SecurityPkg/Library/PlatformSecureLibNull/PlatformSecureLibNull.inf create mode 100644 SecurityPkg/Library/TpmCommLib/CommonHeader.h create mode 100644 SecurityPkg/Library/TpmCommLib/TisPc.c create mode 100644 SecurityPkg/Library/TpmCommLib/TpmComm.c create mode 100644 SecurityPkg/Library/TpmCommLib/TpmCommLib.inf (limited to 'SecurityPkg/Library') diff --git a/SecurityPkg/Library/DxeDeferImageLoadLib/DxeDeferImageLoadLib.c b/SecurityPkg/Library/DxeDeferImageLoadLib/DxeDeferImageLoadLib.c new file mode 100644 index 0000000000..f7fe594060 --- /dev/null +++ b/SecurityPkg/Library/DxeDeferImageLoadLib/DxeDeferImageLoadLib.c @@ -0,0 +1,858 @@ +/** @file + Implement defer image load services for user identification in UEFI2.2. + +Copyright (c) 2009 - 2011, Intel Corporation. All rights reserved.
+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; +} + + +/** + Convert the '/' to '\' in the specified string. + + @param[in, out] Str Points to the string to convert. + +**/ +VOID +ConvertDPStr ( + IN OUT EFI_STRING Str + ) +{ + INTN Count; + INTN Index; + + Count = StrSize(Str) / 2 - 1; + + if (Count < 4) { + return; + } + + // + // Convert device path string. + // + Index = Count - 1; + while (Index > 0) { + // + // Find the last '/'. + // + for (Index = Count - 1; Index > 0; Index--) { + if (Str[Index] == L'/') + break; + } + + // + // Check next char. + // + if (Str[Index + 1] == L'\\') + return; + + Str[Index] = L'\\'; + + // + // Check previous char. + // + if ((Index > 0) && (Str[Index - 1] == L'\\')) { + CopyMem (&Str[Index - 1], &Str[Index], (UINTN) ((Count - Index + 1) * sizeof (CHAR16))); + return; + } + Index--; + } +} + + +/** + 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 + ) +{ + EFI_STATUS Status; + EFI_STRING DevicePathStr1; + EFI_STRING DevicePathStr2; + UINTN StrLen1; + UINTN StrLen2; + EFI_DEVICE_PATH_TO_TEXT_PROTOCOL *DevicePathText; + BOOLEAN DevicePathEqual; + + ASSERT (DevicePath1 != NULL); + ASSERT (DevicePath2 != NULL); + + DevicePathEqual = FALSE; + DevicePathText = NULL; + Status = gBS->LocateProtocol ( + &gEfiDevicePathToTextProtocolGuid, + NULL, + (VOID **) &DevicePathText + ); + ASSERT (Status == EFI_SUCCESS); + + // + // Get first device path string. + // + DevicePathStr1 = DevicePathText->ConvertDevicePathToText (DevicePath1, TRUE, TRUE); + ConvertDPStr (DevicePathStr1); + // + // Get second device path string. + // + DevicePathStr2 = DevicePathText->ConvertDevicePathToText (DevicePath2, TRUE, TRUE); + ConvertDPStr (DevicePathStr2); + + // + // Compare device path string. + // + StrLen1 = StrSize (DevicePathStr1); + StrLen2 = StrSize (DevicePathStr2); + if (StrLen1 > StrLen2) { + DevicePathEqual = FALSE; + goto Done; + } + + if (CompareMem (DevicePathStr1, DevicePathStr2, StrLen1) == 0) { + DevicePathEqual = TRUE; + } + +Done: + FreePool (DevicePathStr1); + FreePool (DevicePathStr2); + 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 TURE 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 TURE 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 TURE 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); + OptionBuffer = GetEfiGlobalVariable (StrTemp); + 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. + + @retval EFI_SUCCESS The file specified by File did authenticate, and the + platform policy dictates that the DXE Core may use File. + @retval EFI_INVALID_PARAMETER File is NULL. + @retval EFI_SECURITY_VIOLATION The file specified by File did not authenticate, and + the platform policy dictates that File should be placed + in the untrusted state. A file may be promoted from + the untrusted to the trusted state at a future time + with a call to the Trust() DXE Service. + @retval EFI_ACCESS_DENIED The file specified by File did not authenticate, and + the platform policy dictates that File should not be + used for any purpose. + +**/ +EFI_STATUS +EFIAPI +DxeDeferImageLoadHandler ( + IN UINT32 AuthenticationStatus, + IN CONST EFI_DEVICE_PATH_PROTOCOL *File, + IN VOID *FileBuffer, + IN UINTN FileSize + ) + +{ + EFI_STATUS Status; + EFI_USER_PROFILE_HANDLE CurrentUser; + UINT32 Policy; + UINT32 FileType; + + if (File == NULL) { + return EFI_INVALID_PARAMETER; + } + + // + // 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_ACCESS_DENIED; + } + 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_ERROR, "[Security] No user identified, the image is deferred to load!\n")); + PutDefferedImageInfo (File, NULL, 0); + + // + // 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 RegisterSecurityHandler ( + DxeDeferImageLoadHandler, + EFI_AUTH_OPERATION_DEFER_IMAGE_LOAD + ); +} + + diff --git a/SecurityPkg/Library/DxeDeferImageLoadLib/DxeDeferImageLoadLib.h b/SecurityPkg/Library/DxeDeferImageLoadLib/DxeDeferImageLoadLib.h new file mode 100644 index 0000000000..52eb81b796 --- /dev/null +++ b/SecurityPkg/Library/DxeDeferImageLoadLib/DxeDeferImageLoadLib.h @@ -0,0 +1,106 @@ +/** @file + The internal header file includes the common header files, defines + internal structure and functions used by DeferImageLoadLib. + +Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.
+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. + +**/ + +#ifndef __DEFER_IMAGE_LOAD_LIB_H__ +#define __DEFER_IMAGE_LOAD_LIB_H__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +// +// Image type definitions. +// +#define IMAGE_UNKNOWN 0x00000001 +#define IMAGE_FROM_FV 0x00000002 +#define IMAGE_FROM_OPTION_ROM 0x00000004 +#define IMAGE_FROM_REMOVABLE_MEDIA 0x00000008 +#define IMAGE_FROM_FIXED_MEDIA 0x00000010 + +// +// The struct to save the deferred image information. +// +typedef struct { + EFI_DEVICE_PATH_PROTOCOL *ImageDevicePath; + VOID *Image; + UINTN ImageSize; + BOOLEAN BootOption; +} DEFERRED_IMAGE_INFO; + +// +// The table to save the deferred image item. +// +typedef struct { + UINTN Count; ///< deferred image count + DEFERRED_IMAGE_INFO *ImageInfo; ///< deferred image item +} DEFERRED_IMAGE_TABLE; + +/** + 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 called successfully. + @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 + ); + +#endif diff --git a/SecurityPkg/Library/DxeDeferImageLoadLib/DxeDeferImageLoadLib.inf b/SecurityPkg/Library/DxeDeferImageLoadLib/DxeDeferImageLoadLib.inf new file mode 100644 index 0000000000..e16fe8dc04 --- /dev/null +++ b/SecurityPkg/Library/DxeDeferImageLoadLib/DxeDeferImageLoadLib.inf @@ -0,0 +1,62 @@ +## @file +# The library instance provides security service of deferring image load. +# +# Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.
+# 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. +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = DxeDeferImageLoadLib + FILE_GUID = 5E2FAE1F-41DA-4fbd-BC81-603CE5CD8497 + MODULE_TYPE = DXE_DRIVER + VERSION_STRING = 1.0 + LIBRARY_CLASS = NULL|DXE_DRIVER UEFI_DRIVER DXE_RUNTIME_DRIVER UEFI_APPLICATION + CONSTRUCTOR = DxeDeferImageLoadLibConstructor + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = IA32 X64 EBC +# + +[Sources] + DxeDeferImageLoadLib.c + DxeDeferImageLoadLib.h + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + SecurityPkg/SecurityPkg.dec + +[LibraryClasses] + UefiRuntimeServicesTableLib + UefiBootServicesTableLib + SecurityManagementLib + MemoryAllocationLib + DevicePathLib + BaseMemoryLib + PrintLib + DebugLib + UefiLib + PcdLib + +[Protocols] + gEfiFirmwareVolume2ProtocolGuid + gEfiBlockIoProtocolGuid + gEfiSimpleFileSystemProtocolGuid + gEfiUserManagerProtocolGuid + gEfiDeferredImageLoadProtocolGuid + gEfiDevicePathToTextProtocolGuid + +[Guids] + gEfiGlobalVariableGuid + +[Pcd] + gEfiSecurityPkgTokenSpaceGuid.PcdDeferImageLoadPolicy diff --git a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.c b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.c new file mode 100644 index 0000000000..148dbd5a89 --- /dev/null +++ b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.c @@ -0,0 +1,1368 @@ +/** @file + Implement image verification services for secure boot service in UEFI2.3.1. + +Copyright (c) 2009 - 2011, Intel Corporation. All rights reserved.
+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 "DxeImageVerificationLib.h" + +EFI_IMAGE_OPTIONAL_HEADER_PTR_UNION mNtHeader; +UINTN mImageSize; +UINT32 mPeCoffHeaderOffset; +UINT8 mImageDigest[MAX_DIGEST_SIZE]; +UINTN mImageDigestSize; +EFI_IMAGE_DATA_DIRECTORY *mSecDataDir = NULL; +UINT8 *mImageBase = NULL; +EFI_GUID mCertType; + +// +// Notify string for authorization UI. +// +CHAR16 mNotifyString1[MAX_NOTIFY_STRING_LEN] = L"Image verification pass but not found in authorized database!"; +CHAR16 mNotifyString2[MAX_NOTIFY_STRING_LEN] = L"Launch this image anyway? (Yes/Defer/No)"; +// +// Public Exponent of RSA Key. +// +CONST UINT8 mRsaE[] = { 0x01, 0x00, 0x01 }; + + +// +// OID ASN.1 Value for Hash Algorithms +// +UINT8 mHashOidValue[] = { + 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x02, 0x05, // OBJ_md5 + 0x2B, 0x0E, 0x03, 0x02, 0x1A, // OBJ_sha1 + 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x04, // OBJ_sha224 + 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, // OBJ_sha256 + 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x02, // OBJ_sha384 + 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, // OBJ_sha512 + }; + +HASH_TABLE mHash[] = { + { L"SHA1", 20, &mHashOidValue[8], 5, Sha1GetContextSize, Sha1Init, Sha1Update, Sha1Final }, + { L"SHA224", 28, &mHashOidValue[13], 9, NULL, NULL, NULL, NULL }, + { L"SHA256", 32, &mHashOidValue[22], 9, Sha256GetContextSize,Sha256Init, Sha256Update, Sha256Final}, + { L"SHA384", 48, &mHashOidValue[31], 9, NULL, NULL, NULL, NULL }, + { L"SHA512", 64, &mHashOidValue[40], 9, NULL, NULL, NULL, NULL } +}; + + +/** + 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 +GetImageType ( + 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; +} + +/** + Caculate hash of Pe/Coff image based on the authenticode image hashing in + PE/COFF Specification 8.0 Appendix A + + @param[in] HashAlg Hash algorithm type. + + @retval TRUE Successfully hash image. + @retval FALSE Fail in hash image. + +**/ +BOOLEAN +HashPeImage ( + IN UINT32 HashAlg + ) +{ + BOOLEAN Status; + UINT16 Magic; + EFI_IMAGE_SECTION_HEADER *Section; + VOID *HashCtx; + UINTN CtxSize; + UINT8 *HashBase; + UINTN HashSize; + UINTN SumOfBytesHashed; + EFI_IMAGE_SECTION_HEADER *SectionHeader; + UINTN Index; + UINTN Pos; + + HashCtx = NULL; + SectionHeader = NULL; + Status = FALSE; + + if ((HashAlg != HASHALG_SHA1) && (HashAlg != HASHALG_SHA256)) { + return FALSE; + } + + // + // Initialize context of hash. + // + ZeroMem (mImageDigest, MAX_DIGEST_SIZE); + + if (HashAlg == HASHALG_SHA1) { + mImageDigestSize = SHA1_DIGEST_SIZE; + mCertType = gEfiCertSha1Guid; + } else if (HashAlg == HASHALG_SHA256) { + mImageDigestSize = SHA256_DIGEST_SIZE; + mCertType = gEfiCertSha256Guid; + } else { + return FALSE; + } + + CtxSize = mHash[HashAlg].GetContextSize(); + + HashCtx = AllocatePool (CtxSize); + ASSERT (HashCtx != NULL); + + // 1. Load the image header into memory. + + // 2. Initialize a SHA hash context. + Status = mHash[HashAlg].HashInit(HashCtx); + + if (!Status) { + goto Done; + } + // + // Measuring PE/COFF Image Header; + // But CheckSum field and SECURITY data directory (certificate) are excluded + // + Magic = mNtHeader.Pe32->OptionalHeader.Magic; + // + // 3. Calculate the distance from the base of the image header to the image checksum address. + // 4. Hash the image header from its base to beginning of the image checksum. + // + HashBase = mImageBase; + if (Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) { + // + // Use PE32 offset. + // + HashSize = (UINTN) ((UINT8 *) (&mNtHeader.Pe32->OptionalHeader.CheckSum) - HashBase); + } else { + // + // Use PE32+ offset. + // + HashSize = (UINTN) ((UINT8 *) (&mNtHeader.Pe32Plus->OptionalHeader.CheckSum) - HashBase); + } + + Status = mHash[HashAlg].HashUpdate(HashCtx, HashBase, HashSize); + if (!Status) { + goto Done; + } + // + // 5. Skip over the image checksum (it occupies a single ULONG). + // 6. Get the address of the beginning of the Cert Directory. + // 7. Hash everything from the end of the checksum to the start of the Cert Directory. + // + if (Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) { + // + // Use PE32 offset. + // + HashBase = (UINT8 *) &mNtHeader.Pe32->OptionalHeader.CheckSum + sizeof (UINT32); + HashSize = (UINTN) ((UINT8 *) (&mNtHeader.Pe32->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY]) - HashBase); + } else { + // + // Use PE32+ offset. + // + HashBase = (UINT8 *) &mNtHeader.Pe32Plus->OptionalHeader.CheckSum + sizeof (UINT32); + HashSize = (UINTN) ((UINT8 *) (&mNtHeader.Pe32Plus->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY]) - HashBase); + } + + Status = mHash[HashAlg].HashUpdate(HashCtx, HashBase, HashSize); + if (!Status) { + goto Done; + } + // + // 8. Skip over the Cert Directory. (It is sizeof(IMAGE_DATA_DIRECTORY) bytes.) + // 9. Hash everything from the end of the Cert Directory to the end of image header. + // + if (Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) { + // + // Use PE32 offset + // + HashBase = (UINT8 *) &mNtHeader.Pe32->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY + 1]; + HashSize = mNtHeader.Pe32->OptionalHeader.SizeOfHeaders - (UINTN) ((UINT8 *) (&mNtHeader.Pe32->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY + 1]) - mImageBase); + } else { + // + // Use PE32+ offset. + // + HashBase = (UINT8 *) &mNtHeader.Pe32Plus->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY + 1]; + HashSize = mNtHeader.Pe32Plus->OptionalHeader.SizeOfHeaders - (UINTN) ((UINT8 *) (&mNtHeader.Pe32Plus->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY + 1]) - mImageBase); + } + + Status = mHash[HashAlg].HashUpdate(HashCtx, HashBase, HashSize); + if (!Status) { + goto Done; + } + // + // 10. Set the SUM_OF_BYTES_HASHED to the size of the header. + // + if (Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) { + // + // Use PE32 offset. + // + SumOfBytesHashed = mNtHeader.Pe32->OptionalHeader.SizeOfHeaders; + } else { + // + // Use PE32+ offset + // + SumOfBytesHashed = mNtHeader.Pe32Plus->OptionalHeader.SizeOfHeaders; + } + + // + // 11. Build a temporary table of pointers to all the IMAGE_SECTION_HEADER + // structures in the image. The 'NumberOfSections' field of the image + // header indicates how big the table should be. Do not include any + // IMAGE_SECTION_HEADERs in the table whose 'SizeOfRawData' field is zero. + // + SectionHeader = (EFI_IMAGE_SECTION_HEADER *) AllocateZeroPool (sizeof (EFI_IMAGE_SECTION_HEADER) * mNtHeader.Pe32->FileHeader.NumberOfSections); + ASSERT (SectionHeader != NULL); + // + // 12. Using the 'PointerToRawData' in the referenced section headers as + // a key, arrange the elements in the table in ascending order. In other + // words, sort the section headers according to the disk-file offset of + // the section. + // + Section = (EFI_IMAGE_SECTION_HEADER *) ( + mImageBase + + mPeCoffHeaderOffset + + sizeof (UINT32) + + sizeof (EFI_IMAGE_FILE_HEADER) + + mNtHeader.Pe32->FileHeader.SizeOfOptionalHeader + ); + for (Index = 0; Index < mNtHeader.Pe32->FileHeader.NumberOfSections; Index++) { + Pos = Index; + while ((Pos > 0) && (Section->PointerToRawData < SectionHeader[Pos - 1].PointerToRawData)) { + CopyMem (&SectionHeader[Pos], &SectionHeader[Pos - 1], sizeof (EFI_IMAGE_SECTION_HEADER)); + Pos--; + } + CopyMem (&SectionHeader[Pos], Section, sizeof (EFI_IMAGE_SECTION_HEADER)); + Section += 1; + } + + // + // 13. Walk through the sorted table, bring the corresponding section + // into memory, and hash the entire section (using the 'SizeOfRawData' + // field in the section header to determine the amount of data to hash). + // 14. Add the section's 'SizeOfRawData' to SUM_OF_BYTES_HASHED . + // 15. Repeat steps 13 and 14 for all the sections in the sorted table. + // + for (Index = 0; Index < mNtHeader.Pe32->FileHeader.NumberOfSections; Index++) { + Section = &SectionHeader[Index]; + if (Section->SizeOfRawData == 0) { + continue; + } + HashBase = mImageBase + Section->PointerToRawData; + HashSize = (UINTN) Section->SizeOfRawData; + + Status = mHash[HashAlg].HashUpdate(HashCtx, HashBase, HashSize); + if (!Status) { + goto Done; + } + + SumOfBytesHashed += HashSize; + } + + // + // 16. If the file size is greater than SUM_OF_BYTES_HASHED, there is extra + // data in the file that needs to be added to the hash. This data begins + // at file offset SUM_OF_BYTES_HASHED and its length is: + // FileSize - (CertDirectory->Size) + // + if (mImageSize > SumOfBytesHashed) { + HashBase = mImageBase + SumOfBytesHashed; + if (Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) { + // + // Use PE32 offset. + // + HashSize = (UINTN)( + mImageSize - + mNtHeader.Pe32->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY].Size - + SumOfBytesHashed); + } else { + // + // Use PE32+ offset. + // + HashSize = (UINTN)( + mImageSize - + mNtHeader.Pe32Plus->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY].Size - + SumOfBytesHashed); + } + + Status = mHash[HashAlg].HashUpdate(HashCtx, HashBase, HashSize); + if (!Status) { + goto Done; + } + } + Status = mHash[HashAlg].HashFinal(HashCtx, mImageDigest); + +Done: + if (HashCtx != NULL) { + FreePool (HashCtx); + } + if (SectionHeader != NULL) { + FreePool (SectionHeader); + } + return Status; +} + +/** + Recognize the Hash algorithm in PE/COFF Authenticode and caculate hash of + Pe/Coff image based on the authenticode image hashing in PE/COFF Specification + 8.0 Appendix A + + @retval EFI_UNSUPPORTED Hash algorithm is not supported. + @retval EFI_SUCCESS Hash successfully. + +**/ +EFI_STATUS +HashPeImageByType ( + VOID + ) +{ + UINT8 Index; + WIN_CERTIFICATE_EFI_PKCS *PkcsCertData; + + PkcsCertData = (WIN_CERTIFICATE_EFI_PKCS *) (mImageBase + mSecDataDir->VirtualAddress); + + for (Index = 0; Index < HASHALG_MAX; Index++) { + // + // Check the Hash algorithm in PE/COFF Authenticode. + // According to PKCS#7 Definition: + // SignedData ::= SEQUENCE { + // version Version, + // digestAlgorithms DigestAlgorithmIdentifiers, + // contentInfo ContentInfo, + // .... } + // The DigestAlgorithmIdentifiers can be used to determine the hash algorithm in PE/COFF hashing + // This field has the fixed offset (+32) in final Authenticode ASN.1 data. + // + if (CompareMem (PkcsCertData->CertData + 32, mHash[Index].OidValue, mHash[Index].OidLength) == 0) { + break; + } + } + + if (Index == HASHALG_MAX) { + return EFI_UNSUPPORTED; + } + + // + // HASH PE Image based on Hash algorithm in PE/COFF Authenticode. + // + if (!HashPeImage(Index)) { + return EFI_UNSUPPORTED; + } + + return EFI_SUCCESS; +} + + +/** + Returns the size of a given image execution info table in bytes. + + This function returns the size, in bytes, of the image execution info table specified by + ImageExeInfoTable. If ImageExeInfoTable is NULL, then 0 is returned. + + @param ImageExeInfoTable A pointer to a image execution info table structure. + + @retval 0 If ImageExeInfoTable is NULL. + @retval Others The size of a image execution info table in bytes. + +**/ +UINTN +GetImageExeInfoTableSize ( + EFI_IMAGE_EXECUTION_INFO_TABLE *ImageExeInfoTable + ) +{ + UINTN Index; + EFI_IMAGE_EXECUTION_INFO *ImageExeInfoItem; + UINTN TotalSize; + + if (ImageExeInfoTable == NULL) { + return 0; + } + + ImageExeInfoItem = (EFI_IMAGE_EXECUTION_INFO *) ((UINT8 *) ImageExeInfoTable + sizeof (EFI_IMAGE_EXECUTION_INFO_TABLE)); + TotalSize = sizeof (EFI_IMAGE_EXECUTION_INFO_TABLE); + for (Index = 0; Index < ImageExeInfoTable->NumberOfImages; Index++) { + TotalSize += ReadUnaligned32 ((UINT32 *) &ImageExeInfoItem->InfoSize); + ImageExeInfoItem = (EFI_IMAGE_EXECUTION_INFO *) ((UINT8 *) ImageExeInfoItem + ReadUnaligned32 ((UINT32 *) &ImageExeInfoItem->InfoSize)); + } + + return TotalSize; +} + +/** + Create an Image Execution Information Table entry and add it to system configuration table. + + @param[in] Action Describes the action taken by the firmware regarding this image. + @param[in] Name Input a null-terminated, user-friendly name. + @param[in] DevicePath Input device path pointer. + @param[in] Signature Input signature info in EFI_SIGNATURE_LIST data structure. + @param[in] SignatureSize Size of signature. + +**/ +VOID +AddImageExeInfo ( + IN EFI_IMAGE_EXECUTION_ACTION Action, + IN CHAR16 *Name OPTIONAL, + IN CONST EFI_DEVICE_PATH_PROTOCOL *DevicePath, + IN EFI_SIGNATURE_LIST *Signature OPTIONAL, + IN UINTN SignatureSize + ) +{ + EFI_STATUS Status; + EFI_IMAGE_EXECUTION_INFO_TABLE *ImageExeInfoTable; + EFI_IMAGE_EXECUTION_INFO_TABLE *NewImageExeInfoTable; + EFI_IMAGE_EXECUTION_INFO *ImageExeInfoEntry; + UINTN ImageExeInfoTableSize; + UINTN NewImageExeInfoEntrySize; + UINTN NameStringLen; + UINTN DevicePathSize; + + ASSERT (DevicePath != NULL); + ImageExeInfoTable = NULL; + NewImageExeInfoTable = NULL; + ImageExeInfoEntry = NULL; + NameStringLen = 0; + + if (Name != NULL) { + NameStringLen = StrSize (Name); + } + + ImageExeInfoTable = NULL; + EfiGetSystemConfigurationTable (&gEfiImageSecurityDatabaseGuid, (VOID**)&ImageExeInfoTable); + if (ImageExeInfoTable != NULL) { + // + // The table has been found! + // We must enlarge the table to accmodate the new exe info entry. + // + ImageExeInfoTableSize = GetImageExeInfoTableSize (ImageExeInfoTable); + } else { + // + // Not Found! + // We should create a new table to append to the configuration table. + // + ImageExeInfoTableSize = sizeof (EFI_IMAGE_EXECUTION_INFO_TABLE); + } + + DevicePathSize = GetDevicePathSize (DevicePath); + NewImageExeInfoEntrySize = sizeof (EFI_IMAGE_EXECUTION_INFO) + NameStringLen + DevicePathSize + SignatureSize; + NewImageExeInfoTable = (EFI_IMAGE_EXECUTION_INFO_TABLE *) AllocateRuntimePool (ImageExeInfoTableSize + NewImageExeInfoEntrySize); + ASSERT (NewImageExeInfoTable != NULL); + + if (ImageExeInfoTable != NULL) { + CopyMem (NewImageExeInfoTable, ImageExeInfoTable, ImageExeInfoTableSize); + } else { + NewImageExeInfoTable->NumberOfImages = 0; + } + NewImageExeInfoTable->NumberOfImages++; + ImageExeInfoEntry = (EFI_IMAGE_EXECUTION_INFO *) ((UINT8 *) NewImageExeInfoTable + ImageExeInfoTableSize); + // + // Update new item's infomation. + // + WriteUnaligned32 ((UINT32 *) &ImageExeInfoEntry->Action, Action); + WriteUnaligned32 ((UINT32 *) &ImageExeInfoEntry->InfoSize, (UINT32) NewImageExeInfoEntrySize); + + if (Name != NULL) { + CopyMem ((UINT8 *) &ImageExeInfoEntry->InfoSize + sizeof (UINT32), Name, NameStringLen); + } + CopyMem ( + (UINT8 *) &ImageExeInfoEntry->InfoSize + sizeof (UINT32) + NameStringLen, + DevicePath, + DevicePathSize + ); + if (Signature != NULL) { + CopyMem ( + (UINT8 *) &ImageExeInfoEntry->InfoSize + sizeof (UINT32) + NameStringLen + DevicePathSize, + Signature, + SignatureSize + ); + } + // + // Update/replace the image execution table. + // + Status = gBS->InstallConfigurationTable (&gEfiImageSecurityDatabaseGuid, (VOID *) NewImageExeInfoTable); + ASSERT_EFI_ERROR (Status); + // + // Free Old table data! + // + if (ImageExeInfoTable != NULL) { + FreePool (ImageExeInfoTable); + } +} + +/** + Discover if the UEFI image is authorized by user's policy setting. + + @param[in] Policy Specify platform's policy setting. + + @retval EFI_ACCESS_DENIED Image is not allowed to run. + @retval EFI_SECURITY_VIOLATION Image is deferred. + @retval EFI_SUCCESS Image is authorized to run. + +**/ +EFI_STATUS +ImageAuthorization ( + IN UINT32 Policy + ) +{ + EFI_STATUS Status; + EFI_INPUT_KEY Key; + + Status = EFI_ACCESS_DENIED; + + switch (Policy) { + + case QUERY_USER_ON_SECURITY_VIOLATION: + do { + CreatePopUp (EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE, &Key, mNotifyString1, mNotifyString2, NULL); + if (Key.UnicodeChar == L'Y' || Key.UnicodeChar == L'y') { + Status = EFI_SUCCESS; + break; + } else if (Key.UnicodeChar == L'N' || Key.UnicodeChar == L'n') { + Status = EFI_ACCESS_DENIED; + break; + } else if (Key.UnicodeChar == L'D' || Key.UnicodeChar == L'd') { + Status = EFI_SECURITY_VIOLATION; + break; + } + } while (TRUE); + break; + + case ALLOW_EXECUTE_ON_SECURITY_VIOLATION: + Status = EFI_SUCCESS; + break; + + case DEFER_EXECUTE_ON_SECURITY_VIOLATION: + Status = EFI_SECURITY_VIOLATION; + break; + + case DENY_EXECUTE_ON_SECURITY_VIOLATION: + Status = EFI_ACCESS_DENIED; + break; + } + + return Status; +} + +/** + Check whether signature is in specified database. + + @param[in] VariableName Name of database variable that is searched in. + @param[in] Signature Pointer to signature that is searched for. + @param[in] CertType Pointer to hash algrithom. + @param[in] SignatureSize Size of Signature. + + @return TRUE Found the signature in the variable database. + @return FALSE Not found the signature in the variable database. + +**/ +BOOLEAN +IsSignatureFoundInDatabase ( + IN CHAR16 *VariableName, + IN UINT8 *Signature, + IN EFI_GUID *CertType, + IN UINTN SignatureSize + ) +{ + EFI_STATUS Status; + EFI_SIGNATURE_LIST *CertList; + EFI_SIGNATURE_DATA *Cert; + UINTN DataSize; + UINT8 *Data; + UINTN Index; + UINTN CertCount; + BOOLEAN IsFound; + // + // Read signature database variable. + // + IsFound = FALSE; + Data = NULL; + DataSize = 0; + Status = gRT->GetVariable (VariableName, &gEfiImageSecurityDatabaseGuid, NULL, &DataSize, NULL); + if (Status != EFI_BUFFER_TOO_SMALL) { + return FALSE; + } + + Data = (UINT8 *) AllocateZeroPool (DataSize); + ASSERT (Data != NULL); + + Status = gRT->GetVariable (VariableName, &gEfiImageSecurityDatabaseGuid, NULL, &DataSize, Data); + if (EFI_ERROR (Status)) { + goto Done; + } + // + // Enumerate all signature data in SigDB to check if executable's signature exists. + // + CertList = (EFI_SIGNATURE_LIST *) Data; + while ((DataSize > 0) && (DataSize >= CertList->SignatureListSize)) { + CertCount = (CertList->SignatureListSize - CertList->SignatureHeaderSize) / CertList->SignatureSize; + Cert = (EFI_SIGNATURE_DATA *) ((UINT8 *) CertList + sizeof (EFI_SIGNATURE_LIST) + CertList->SignatureHeaderSize); + if ((CertList->SignatureSize == sizeof(EFI_SIGNATURE_DATA) - 1 + SignatureSize) && (CompareGuid(&CertList->SignatureType, CertType))) { + for (Index = 0; Index < CertCount; Index++) { + if (CompareMem (Cert->SignatureData, Signature, SignatureSize) == 0) { + // + // Find the signature in database. + // + IsFound = TRUE; + break; + } + + Cert = (EFI_SIGNATURE_DATA *) ((UINT8 *) Cert + CertList->SignatureSize); + } + + if (IsFound) { + break; + } + } + + DataSize -= CertList->SignatureListSize; + CertList = (EFI_SIGNATURE_LIST *) ((UINT8 *) CertList + CertList->SignatureListSize); + } + +Done: + if (Data != NULL) { + FreePool (Data); + } + + return IsFound; +} + +/** + Verify certificate in WIN_CERT_TYPE_PKCS_SIGNED_DATA format . + + @retval EFI_SUCCESS Image pass verification. + @retval EFI_SECURITY_VIOLATION Image fail verification. + @retval other error value + +**/ +EFI_STATUS +VerifyCertPkcsSignedData ( + VOID + ) +{ + EFI_STATUS Status; + BOOLEAN VerifyStatus; + WIN_CERTIFICATE_EFI_PKCS *PkcsCertData; + EFI_SIGNATURE_LIST *CertList; + EFI_SIGNATURE_DATA *Cert; + UINTN DataSize; + UINT8 *Data; + UINT8 *RootCert; + UINTN RootCertSize; + UINTN Index; + UINTN CertCount; + + Data = NULL; + CertList = NULL; + Cert = NULL; + RootCert = NULL; + RootCertSize = 0; + VerifyStatus = FALSE; + PkcsCertData = (WIN_CERTIFICATE_EFI_PKCS *) (mImageBase + mSecDataDir->VirtualAddress); + + // + // 1: Find certificate from KEK database and try to verify authenticode struct. + // + DataSize = 0; + Status = gRT->GetVariable (EFI_KEY_EXCHANGE_KEY_NAME, &gEfiGlobalVariableGuid, NULL, &DataSize, NULL); + if (Status == EFI_BUFFER_TOO_SMALL) { + Data = (UINT8 *)AllocateZeroPool (DataSize); + ASSERT (Data != NULL); + + Status = gRT->GetVariable (EFI_KEY_EXCHANGE_KEY_NAME, &gEfiGlobalVariableGuid, NULL, &DataSize, (VOID *)Data); + if (EFI_ERROR (Status)) { + goto Done; + } + + // + // Find Cert Enrolled in KEK database to verify the signature in pkcs7 signed data. + // + CertList = (EFI_SIGNATURE_LIST *) Data; + while ((DataSize > 0) && (DataSize >= CertList->SignatureListSize)) { + if (CompareGuid (&CertList->SignatureType, &gEfiCertX509Guid)) { + Cert = (EFI_SIGNATURE_DATA *) ((UINT8 *) CertList + sizeof (EFI_SIGNATURE_LIST) + CertList->SignatureHeaderSize); + CertCount = (CertList->SignatureListSize - sizeof (EFI_SIGNATURE_LIST) - CertList->SignatureHeaderSize) / CertList->SignatureSize; + for (Index = 0; Index < CertCount; Index++) { + // + // Iterate each Signature Data Node within this CertList for a verify + // + RootCert = Cert->SignatureData; + RootCertSize = CertList->SignatureSize; + + // + // Call AuthenticodeVerify library to Verify Authenticode struct. + // + VerifyStatus = AuthenticodeVerify ( + PkcsCertData->CertData, + mSecDataDir->Size - sizeof(PkcsCertData->Hdr), + RootCert, + RootCertSize, + mImageDigest, + mImageDigestSize + ); + + if (VerifyStatus) { + goto Done; + } + Cert = (EFI_SIGNATURE_DATA *) ((UINT8 *) Cert + CertList->SignatureSize); + } + } + DataSize -= CertList->SignatureListSize; + CertList = (EFI_SIGNATURE_LIST *) ((UINT8 *) CertList + CertList->SignatureListSize); + } + } + + + + // + // 2: Find certificate from DB database and try to verify authenticode struct. + // + DataSize = 0; + Status = gRT->GetVariable (EFI_IMAGE_SECURITY_DATABASE, &gEfiImageSecurityDatabaseGuid, NULL, &DataSize, NULL); + if (Status == EFI_BUFFER_TOO_SMALL) { + Data = (UINT8 *)AllocateZeroPool (DataSize); + ASSERT (Data != NULL); + + Status = gRT->GetVariable (EFI_IMAGE_SECURITY_DATABASE, &gEfiImageSecurityDatabaseGuid, NULL, &DataSize, (VOID *)Data); + if (EFI_ERROR (Status)) { + goto Done; + } + + // + // Find Cert Enrolled in DB database to verify the signature in pkcs7 signed data. + // + CertList = (EFI_SIGNATURE_LIST *) Data; + while ((DataSize > 0) && (DataSize >= CertList->SignatureListSize)) { + if (CompareGuid (&CertList->SignatureType, &gEfiCertX509Guid)) { + Cert = (EFI_SIGNATURE_DATA *) ((UINT8 *) CertList + sizeof (EFI_SIGNATURE_LIST) + CertList->SignatureHeaderSize); + CertCount = (CertList->SignatureListSize - sizeof (EFI_SIGNATURE_LIST) - CertList->SignatureHeaderSize) / CertList->SignatureSize; + for (Index = 0; Index < CertCount; Index++) { + // + // Iterate each Signature Data Node within this CertList for a verify + // + RootCert = Cert->SignatureData; + RootCertSize = CertList->SignatureSize; + + // + // Call AuthenticodeVerify library to Verify Authenticode struct. + // + VerifyStatus = AuthenticodeVerify ( + PkcsCertData->CertData, + mSecDataDir->Size - sizeof(PkcsCertData->Hdr), + RootCert, + RootCertSize, + mImageDigest, + mImageDigestSize + ); + + if (VerifyStatus) { + goto Done; + } + Cert = (EFI_SIGNATURE_DATA *) ((UINT8 *) Cert + CertList->SignatureSize); + } + } + DataSize -= CertList->SignatureListSize; + CertList = (EFI_SIGNATURE_LIST *) ((UINT8 *) CertList + CertList->SignatureListSize); + } + } + +Done: + if (Data != NULL) { + FreePool (Data); + } + + if (VerifyStatus) { + return EFI_SUCCESS; + } else { + return EFI_SECURITY_VIOLATION; + } +} + +/** + Verify certificate in WIN_CERTIFICATE_UEFI_GUID format. + + @retval EFI_SUCCESS Image pass verification. + @retval EFI_SECURITY_VIOLATION Image fail verification. + @retval other error value + +**/ +EFI_STATUS +VerifyCertUefiGuid ( + VOID + ) +{ + BOOLEAN Status; + WIN_CERTIFICATE_UEFI_GUID *EfiCert; + EFI_SIGNATURE_LIST *KekList; + EFI_SIGNATURE_DATA *KekItem; + EFI_CERT_BLOCK_RSA_2048_SHA256 *CertBlock; + VOID *Rsa; + UINTN KekCount; + UINTN Index; + UINTN KekDataSize; + BOOLEAN IsFound; + EFI_STATUS Result; + + EfiCert = NULL; + KekList = NULL; + KekItem = NULL; + CertBlock = NULL; + Rsa = NULL; + Status = FALSE; + IsFound = FALSE; + KekDataSize = 0; + + EfiCert = (WIN_CERTIFICATE_UEFI_GUID *) (mImageBase + mSecDataDir->VirtualAddress); + CertBlock = (EFI_CERT_BLOCK_RSA_2048_SHA256 *) EfiCert->CertData; + if (!CompareGuid (&EfiCert->CertType, &gEfiCertTypeRsa2048Sha256Guid)) { + // + // Invalid Certificate Data Type. + // + return EFI_SECURITY_VIOLATION; + } + + // + // Get KEK database variable data size + // + Result = gRT->GetVariable (EFI_KEY_EXCHANGE_KEY_NAME, &gEfiGlobalVariableGuid, NULL, &KekDataSize, NULL); + if (Result != EFI_BUFFER_TOO_SMALL) { + return EFI_SECURITY_VIOLATION; + } + + // + // Get KEK database variable. + // + KekList = GetEfiGlobalVariable (EFI_KEY_EXCHANGE_KEY_NAME); + if (KekList == NULL) { + return EFI_SECURITY_VIOLATION; + } + + // + // Enumerate all Kek items in this list to verify the variable certificate data. + // If anyone is authenticated successfully, it means the variable is correct! + // + while ((KekDataSize > 0) && (KekDataSize >= KekList->SignatureListSize)) { + if (CompareGuid (&KekList->SignatureType, &gEfiCertRsa2048Guid)) { + KekItem = (EFI_SIGNATURE_DATA *) ((UINT8 *) KekList + sizeof (EFI_SIGNATURE_LIST) + KekList->SignatureHeaderSize); + KekCount = (KekList->SignatureListSize - sizeof (EFI_SIGNATURE_LIST) - KekList->SignatureHeaderSize) / KekList->SignatureSize; + for (Index = 0; Index < KekCount; Index++) { + if (CompareMem (KekItem->SignatureData, CertBlock->PublicKey, EFI_CERT_TYPE_RSA2048_SIZE) == 0) { + IsFound = TRUE; + break; + } + KekItem = (EFI_SIGNATURE_DATA *) ((UINT8 *) KekItem + KekList->SignatureSize); + } + } + KekDataSize -= KekList->SignatureListSize; + KekList = (EFI_SIGNATURE_LIST *) ((UINT8 *) KekList + KekList->SignatureListSize); + } + + if (!IsFound) { + // + // Signed key is not a trust one. + // + goto Done; + } + + // + // Now, we found the corresponding security policy. + // Verify the data payload. + // + Rsa = RsaNew (); + ASSERT (Rsa != NULL); + // + // Set RSA Key Components. + // NOTE: Only N and E are needed to be set as RSA public key for signature verification. + // + Status = RsaSetKey (Rsa, RsaKeyN, CertBlock->PublicKey, EFI_CERT_TYPE_RSA2048_SIZE); + if (!Status) { + goto Done; + } + Status = RsaSetKey (Rsa, RsaKeyE, mRsaE, sizeof (mRsaE)); + if (!Status) { + goto Done; + } + // + // Verify the signature. + // + Status = RsaPkcs1Verify ( + Rsa, + mImageDigest, + mImageDigestSize, + CertBlock->Signature, + EFI_CERT_TYPE_RSA2048_SHA256_SIZE + ); + +Done: + if (KekList != NULL) { + FreePool (KekList); + } + if (Rsa != NULL ) { + RsaFree (Rsa); + } + if (Status) { + return EFI_SUCCESS; + } else { + return EFI_SECURITY_VIOLATION; + } +} + +/** + Provide verification service for signed images, which include both signature validation + and platform policy control. For signature types, both UEFI WIN_CERTIFICATE_UEFI_GUID and + MSFT Authenticode type signatures are supported. + + In this implementation, only verify external executables when in USER MODE. + Executables from FV is bypass, so pass in AuthenticationStatus is ignored. + + @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. + + @retval EFI_SUCCESS The file specified by File did authenticate, and the + platform policy dictates that the DXE Core may use File. + @retval EFI_INVALID_PARAMETER File is NULL. + @retval EFI_SECURITY_VIOLATION The file specified by File did not authenticate, and + the platform policy dictates that File should be placed + in the untrusted state. A file may be promoted from + the untrusted to the trusted state at a future time + with a call to the Trust() DXE Service. + @retval EFI_ACCESS_DENIED The file specified by File did not authenticate, and + the platform policy dictates that File should not be + used for any purpose. + +**/ +EFI_STATUS +EFIAPI +DxeImageVerificationHandler ( + IN UINT32 AuthenticationStatus, + IN CONST EFI_DEVICE_PATH_PROTOCOL *File, + IN VOID *FileBuffer, + IN UINTN FileSize + ) + +{ + EFI_STATUS Status; + UINT16 Magic; + EFI_IMAGE_DOS_HEADER *DosHdr; + EFI_STATUS VerifyStatus; + UINT8 *SetupMode; + EFI_SIGNATURE_LIST *SignatureList; + UINTN SignatureListSize; + EFI_SIGNATURE_DATA *Signature; + EFI_IMAGE_EXECUTION_ACTION Action; + WIN_CERTIFICATE *WinCertificate; + UINT32 Policy; + + if (File == NULL) { + return EFI_INVALID_PARAMETER; + } + + SignatureList = NULL; + SignatureListSize = 0; + WinCertificate = NULL; + Action = EFI_IMAGE_EXECUTION_AUTH_UNTESTED; + Status = EFI_ACCESS_DENIED; + // + // Check the image type and get policy setting. + // + switch (GetImageType (File)) { + + case IMAGE_FROM_FV: + Policy = ALWAYS_EXECUTE; + break; + + case IMAGE_FROM_OPTION_ROM: + Policy = PcdGet32 (PcdOptionRomImageVerificationPolicy); + break; + + case IMAGE_FROM_REMOVABLE_MEDIA: + Policy = PcdGet32 (PcdRemovableMediaImageVerificationPolicy); + break; + + case IMAGE_FROM_FIXED_MEDIA: + Policy = PcdGet32 (PcdFixedMediaImageVerificationPolicy); + break; + + default: + Policy = DENY_EXECUTE_ON_SECURITY_VIOLATION; + break; + } + // + // If policy is always/never execute, return directly. + // + if (Policy == ALWAYS_EXECUTE) { + return EFI_SUCCESS; + } else if (Policy == NEVER_EXECUTE) { + return EFI_ACCESS_DENIED; + } + SetupMode = GetEfiGlobalVariable (EFI_SETUP_MODE_NAME); + + // + // SetupMode doesn't exist means no AuthVar driver is dispatched, + // skip verification. + // + if (SetupMode == NULL) { + return EFI_SUCCESS; + } + + // + // If platform is in SETUP MODE, skip verification. + // + if (*SetupMode == SETUP_MODE) { + FreePool (SetupMode); + return EFI_SUCCESS; + } + // + // Read the Dos header. + // + ASSERT (FileBuffer != NULL); + mImageBase = (UINT8 *) FileBuffer; + mImageSize = FileSize; + DosHdr = (EFI_IMAGE_DOS_HEADER *) (mImageBase); + if (DosHdr->e_magic == EFI_IMAGE_DOS_SIGNATURE) { + // + // DOS image header is present, + // so read the PE header after the DOS image header. + // + mPeCoffHeaderOffset = DosHdr->e_lfanew; + } else { + mPeCoffHeaderOffset = 0; + } + // + // Check PE/COFF image. + // + mNtHeader.Pe32 = (EFI_IMAGE_NT_HEADERS32 *) (mImageBase + mPeCoffHeaderOffset); + if (mNtHeader.Pe32->Signature != EFI_IMAGE_NT_SIGNATURE) { + // + // It is not a valid Pe/Coff file. + // + return EFI_ACCESS_DENIED; + } + + Magic = mNtHeader.Pe32->OptionalHeader.Magic; + if (Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) { + // + // Use PE32 offset. + // + mSecDataDir = (EFI_IMAGE_DATA_DIRECTORY *)&mNtHeader.Pe32->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY]; + } else { + // + // Use PE32+ offset. + // + mSecDataDir = (EFI_IMAGE_DATA_DIRECTORY *)&mNtHeader.Pe32Plus->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY]; + } + + if (mSecDataDir->Size == 0) { + // + // This image is not signed. + // + Action = EFI_IMAGE_EXECUTION_AUTH_UNTESTED; + Status = EFI_ACCESS_DENIED; + goto Done; + } + // + // Verify signature of executables. + // + WinCertificate = (WIN_CERTIFICATE *) (mImageBase + mSecDataDir->VirtualAddress); + + switch (WinCertificate->wCertificateType) { + + case WIN_CERT_TYPE_EFI_GUID: + // + // Verify UEFI GUID type. + // + if (!HashPeImage (HASHALG_SHA256)) { + goto Done; + } + + VerifyStatus = VerifyCertUefiGuid (); + break; + + case WIN_CERT_TYPE_PKCS_SIGNED_DATA: + // + // Verify Pkcs signed data type. + // + Status = HashPeImageByType(); + if (EFI_ERROR(Status)) { + goto Done; + } + + VerifyStatus = VerifyCertPkcsSignedData (); + + // + // For image verification against enrolled certificate(root or intermediate), + // no need to check image's hash in the allowed database. + // + if (!EFI_ERROR (VerifyStatus)) { + return EFI_SUCCESS; + } + + default: + return EFI_ACCESS_DENIED; + } + // + // Get image hash value as executable's signature. + // + SignatureListSize = sizeof (EFI_SIGNATURE_LIST) + sizeof (EFI_SIGNATURE_DATA) - 1 + mImageDigestSize; + SignatureList = (EFI_SIGNATURE_LIST *) AllocateZeroPool (SignatureListSize); + ASSERT (SignatureList != NULL); + SignatureList->SignatureHeaderSize = 0; + SignatureList->SignatureListSize = (UINT32) SignatureListSize; + SignatureList->SignatureSize = (UINT32) mImageDigestSize; + CopyMem (&SignatureList->SignatureType, &mCertType, sizeof (EFI_GUID)); + Signature = (EFI_SIGNATURE_DATA *) ((UINT8 *) SignatureList + sizeof (EFI_SIGNATURE_LIST)); + CopyMem (Signature->SignatureData, mImageDigest, mImageDigestSize); + // + // Signature database check after verification. + // + if (EFI_ERROR (VerifyStatus)) { + // + // Verification failure. + // + Action = EFI_IMAGE_EXECUTION_AUTH_SIG_FAILED; + Status = EFI_ACCESS_DENIED; + } else if (IsSignatureFoundInDatabase (EFI_IMAGE_SECURITY_DATABASE1, Signature->SignatureData, &mCertType, mImageDigestSize)) { + // + // Executable signature verification passes, but is found in forbidden signature database. + // + Action = EFI_IMAGE_EXECUTION_AUTH_SIG_FOUND; + Status = EFI_ACCESS_DENIED; + } else if (IsSignatureFoundInDatabase (EFI_IMAGE_SECURITY_DATABASE, Signature->SignatureData, &mCertType, mImageDigestSize)) { + // + // Executable signature is found in authorized signature database. + // + Status = EFI_SUCCESS; + } else { + // + // Executable signature verification passes, but cannot be found in authorized signature database. + // Get platform policy to determine the action. + // + Action = EFI_IMAGE_EXECUTION_AUTH_SIG_PASSED; + Status = ImageAuthorization (Policy); + } + +Done: + if (Status != EFI_SUCCESS) { + // + // Policy decides to defer or reject the image; add its information in image executable information table. + // + AddImageExeInfo (Action, NULL, File, SignatureList, SignatureListSize); + } + + if (SignatureList != NULL) { + FreePool (SignatureList); + } + + FreePool (SetupMode); + + return Status; +} + +/** + When VariableWriteArchProtocol install, create "SecureBoot" variable. + + @param[in] Event Event whose notification function is being invoked. + @param[in] Context Pointer to the notification function's context. + +**/ +VOID +EFIAPI +VariableWriteCallBack ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + UINT8 SecureBootMode; + UINT8 *SecureBootModePtr; + EFI_STATUS Status; + VOID *ProtocolPointer; + + Status = gBS->LocateProtocol (&gEfiVariableWriteArchProtocolGuid, NULL, &ProtocolPointer); + if (EFI_ERROR (Status)) { + return; + } + + // + // Check whether "SecureBoot" variable exists. + // If this library is built-in, it means firmware has capability to perform + // driver signing verification. + // + SecureBootModePtr = GetEfiGlobalVariable (EFI_SECURE_BOOT_MODE_NAME); + if (SecureBootModePtr == NULL) { + SecureBootMode = SECURE_BOOT_MODE_DISABLE; + // + // Authenticated variable driver will update "SecureBoot" depending on SetupMode variable. + // + gRT->SetVariable ( + EFI_SECURE_BOOT_MODE_NAME, + &gEfiGlobalVariableGuid, + EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE, + sizeof (UINT8), + &SecureBootMode + ); + } else { + FreePool (SecureBootModePtr); + } +} + +/** + Register security measurement handler. + + @param ImageHandle ImageHandle of the loaded driver. + @param SystemTable Pointer to the EFI System Table. + + @retval EFI_SUCCESS The handlers were registered successfully. +**/ +EFI_STATUS +EFIAPI +DxeImageVerificationLibConstructor ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + VOID *Registration; + + // + // Register callback function upon VariableWriteArchProtocol. + // + EfiCreateProtocolNotifyEvent ( + &gEfiVariableWriteArchProtocolGuid, + TPL_CALLBACK, + VariableWriteCallBack, + NULL, + &Registration + ); + + return RegisterSecurityHandler ( + DxeImageVerificationHandler, + EFI_AUTH_OPERATION_VERIFY_IMAGE | EFI_AUTH_OPERATION_IMAGE_REQUIRED + ); +} diff --git a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.h b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.h new file mode 100644 index 0000000000..34ed0c89a1 --- /dev/null +++ b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.h @@ -0,0 +1,201 @@ +/** @file + The internal header file includes the common header files, defines + internal structure and functions used by ImageVerificationLib. + +Copyright (c) 2009 - 2011, Intel Corporation. All rights reserved.
+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. + +**/ + +#ifndef __IMAGEVERIFICATIONLIB_H__ +#define __IMAGEVERIFICATIONLIB_H__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define EFI_CERT_TYPE_RSA2048_SHA256_SIZE 256 +#define EFI_CERT_TYPE_RSA2048_SIZE 256 +#define MAX_NOTIFY_STRING_LEN 64 + +// +// Image type definitions +// +#define IMAGE_UNKNOWN 0x00000000 +#define IMAGE_FROM_FV 0x00000001 +#define IMAGE_FROM_OPTION_ROM 0x00000002 +#define IMAGE_FROM_REMOVABLE_MEDIA 0x00000003 +#define IMAGE_FROM_FIXED_MEDIA 0x00000004 + +// +// Authorization policy bit definition +// +#define ALWAYS_EXECUTE 0x00000000 +#define NEVER_EXECUTE 0x00000001 +#define ALLOW_EXECUTE_ON_SECURITY_VIOLATION 0x00000002 +#define DEFER_EXECUTE_ON_SECURITY_VIOLATION 0x00000003 +#define DENY_EXECUTE_ON_SECURITY_VIOLATION 0x00000004 +#define QUERY_USER_ON_SECURITY_VIOLATION 0x00000005 + +// +// Support hash types +// +#define HASHALG_SHA1 0x00000000 +#define HASHALG_SHA224 0x00000001 +#define HASHALG_SHA256 0x00000002 +#define HASHALG_SHA384 0x00000003 +#define HASHALG_SHA512 0x00000004 +#define HASHALG_MAX 0x00000005 + +// +// Set max digest size as SHA256 Output (32 bytes) by far +// +#define MAX_DIGEST_SIZE SHA256_DIGEST_SIZE +// +// +// PKCS7 Certificate definition +// +typedef struct { + WIN_CERTIFICATE Hdr; + UINT8 CertData[1]; +} WIN_CERTIFICATE_EFI_PKCS; + + +/** + Retrieves the size, in bytes, of the context buffer required for hash operations. + + @return The size, in bytes, of the context buffer required for hash operations. + +**/ +typedef +UINTN +(EFIAPI *HASH_GET_CONTEXT_SIZE)( + VOID + ); + +/** + Initializes user-supplied memory pointed by HashContext as hash context for + subsequent use. + + If HashContext is NULL, then ASSERT(). + + @param[in, out] HashContext Pointer to Context being initialized. + + @retval TRUE HASH context initialization succeeded. + @retval FALSE HASH context initialization failed. + +**/ +typedef +BOOLEAN +(EFIAPI *HASH_INIT)( + IN OUT VOID *HashContext + ); + + +/** + Performs digest on a data buffer of the specified length. This function can + be called multiple times to compute the digest of long or discontinuous data streams. + + If HashContext is NULL, then ASSERT(). + + @param[in, out] HashContext Pointer to the MD5 context. + @param[in] Data Pointer to the buffer containing the data to be hashed. + @param[in] DataLength Length of Data buffer in bytes. + + @retval TRUE HASH data digest succeeded. + @retval FALSE Invalid HASH context. After HashFinal function has been called, the + HASH context cannot be reused. + +**/ +typedef +BOOLEAN +(EFIAPI *HASH_UPDATE)( + IN OUT VOID *HashContext, + IN CONST VOID *Data, + IN UINTN DataLength + ); + +/** + Completes hash computation and retrieves the digest value into the specified + memory. After this function has been called, the context cannot be used again. + + If HashContext is NULL, then ASSERT(). + If HashValue is NULL, then ASSERT(). + + @param[in, out] HashContext Pointer to the MD5 context + @param[out] HashValue Pointer to a buffer that receives the HASH digest + value. + + @retval TRUE HASH digest computation succeeded. + @retval FALSE HASH digest computation failed. + +**/ +typedef +BOOLEAN +(EFIAPI *HASH_FINAL)( + IN OUT VOID *HashContext, + OUT UINT8 *HashValue + ); + + +// +// Hash Algorithm Table +// +typedef struct { + // + // Name for Hash Algorithm + // + CHAR16 *Name; + // + // Digest Length + // + UINTN DigestLength; + // + // Hash Algorithm OID ASN.1 Value + // + UINT8 *OidValue; + // + // Length of Hash OID Value + // + UINTN OidLength; + // + // Pointer to Hash GetContentSize function + // + HASH_GET_CONTEXT_SIZE GetContextSize; + // + // Pointer to Hash Init function + // + HASH_INIT HashInit; + // + // Pointer to Hash Update function + // + HASH_UPDATE HashUpdate; + // + // Pointer to Hash Final function + // + HASH_FINAL HashFinal; +} HASH_TABLE; + +#endif diff --git a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.inf b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.inf new file mode 100644 index 0000000000..5874d6b66b --- /dev/null +++ b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.inf @@ -0,0 +1,73 @@ +## @file +# The library instance provides security service of image verification. +# Image verification Library module supports UEFI2.3.1 +# +# Copyright (c) 2009 - 2011, Intel Corporation. All rights reserved.
+# 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. +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = DxeImageVerificationLib + FILE_GUID = 0CA970E1-43FA-4402-BC0A-81AF336BFFD6 + MODULE_TYPE = DXE_DRIVER + VERSION_STRING = 1.0 + LIBRARY_CLASS = NULL|DXE_DRIVER DXE_RUNTIME_DRIVER DXE_SAL_DRIVER DXE_SMM_DRIVER UEFI_APPLICATION UEFI_DRIVER + CONSTRUCTOR = DxeImageVerificationLibConstructor + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = IA32 X64 IPF EBC +# + +[Sources] + DxeImageVerificationLib.c + DxeImageVerificationLib.h + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + CryptoPkg/CryptoPkg.dec + SecurityPkg/SecurityPkg.dec + +[LibraryClasses] + MemoryAllocationLib + BaseLib + UefiLib + UefiBootServicesTableLib + UefiRuntimeServicesTableLib + BaseMemoryLib + DebugLib + DevicePathLib + BaseCryptLib + SecurityManagementLib + +[Protocols] + gEfiFirmwareVolume2ProtocolGuid + gEfiBlockIoProtocolGuid + gEfiSimpleFileSystemProtocolGuid + gEfiVariableWriteArchProtocolGuid + +[Guids] + gEfiCertTypeRsa2048Sha256Guid + gEfiImageSecurityDatabaseGuid + gEfiCertSha1Guid + gEfiCertSha256Guid + gEfiCertX509Guid + gEfiCertRsa2048Guid + +[Pcd] + gEfiSecurityPkgTokenSpaceGuid.PcdOptionRomImageVerificationPolicy + gEfiSecurityPkgTokenSpaceGuid.PcdRemovableMediaImageVerificationPolicy + gEfiSecurityPkgTokenSpaceGuid.PcdFixedMediaImageVerificationPolicy + + + + diff --git a/SecurityPkg/Library/DxeTpmMeasureBootLib/DxeTpmMeasureBootLib.c b/SecurityPkg/Library/DxeTpmMeasureBootLib/DxeTpmMeasureBootLib.c new file mode 100644 index 0000000000..c012f130d9 --- /dev/null +++ b/SecurityPkg/Library/DxeTpmMeasureBootLib/DxeTpmMeasureBootLib.c @@ -0,0 +1,830 @@ +/** @file + The library instance provides security service of TPM measure boot. + +Copyright (c) 2009 - 2011, Intel Corporation. All rights reserved.
+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 + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// +// Flag to check GPT partition. It only need be measured once. +// +BOOLEAN mMeasureGptTableFlag = FALSE; +EFI_GUID mZeroGuid = {0, 0, 0, {0, 0, 0, 0, 0, 0, 0, 0}}; +UINTN mMeasureGptCount = 0; + +/** + Reads contents of a PE/COFF image in memory buffer. + + @param FileHandle Pointer to the file handle to read the PE/COFF image. + @param FileOffset Offset into the PE/COFF image to begin the read operation. + @param ReadSize On input, the size in bytes of the requested read operation. + On output, the number of bytes actually read. + @param Buffer Output buffer that contains the data read from the PE/COFF image. + + @retval EFI_SUCCESS The specified portion of the PE/COFF image was read and the size +**/ +EFI_STATUS +EFIAPI +ImageRead ( + IN VOID *FileHandle, + IN UINTN FileOffset, + IN OUT UINTN *ReadSize, + OUT VOID *Buffer + ) +{ + CopyMem (Buffer, (UINT8 *)((UINTN) FileHandle + FileOffset), *ReadSize); + return EFI_SUCCESS; +} + +/** + Measure GPT table data into TPM log. + + @param TcgProtocol Pointer to the located TCG protocol instance. + @param GptHandle Handle that GPT partition was installed. + + @retval EFI_SUCCESS Successfully measure GPT table. + @retval EFI_UNSUPPORTED Not support GPT table on the given handle. + @retval EFI_DEVICE_ERROR Can't get GPT table because device error. + @retval EFI_OUT_OF_RESOURCES No enough resource to measure GPT table. + @retval other error value +**/ +EFI_STATUS +EFIAPI +TcgMeasureGptTable ( + IN EFI_TCG_PROTOCOL *TcgProtocol, + IN EFI_HANDLE GptHandle + ) +{ + EFI_STATUS Status; + EFI_BLOCK_IO_PROTOCOL *BlockIo; + EFI_DISK_IO_PROTOCOL *DiskIo; + EFI_PARTITION_TABLE_HEADER *PrimaryHeader; + EFI_PARTITION_ENTRY *PartitionEntry; + UINT8 *EntryPtr; + UINTN NumberOfPartition; + UINT32 Index; + TCG_PCR_EVENT *TcgEvent; + EFI_GPT_DATA *GptData; + UINT32 EventSize; + UINT32 EventNumber; + EFI_PHYSICAL_ADDRESS EventLogLastEntry; + + if (mMeasureGptCount > 0) { + return EFI_SUCCESS; + } + + Status = gBS->HandleProtocol (GptHandle, &gEfiBlockIoProtocolGuid, (VOID**)&BlockIo); + if (EFI_ERROR (Status)) { + return EFI_UNSUPPORTED; + } + Status = gBS->HandleProtocol (GptHandle, &gEfiDiskIoProtocolGuid, (VOID**)&DiskIo); + if (EFI_ERROR (Status)) { + return EFI_UNSUPPORTED; + } + // + // Read the EFI Partition Table Header + // + PrimaryHeader = (EFI_PARTITION_TABLE_HEADER *) AllocatePool (BlockIo->Media->BlockSize); + if (PrimaryHeader == NULL) { + return EFI_OUT_OF_RESOURCES; + } + Status = DiskIo->ReadDisk ( + DiskIo, + BlockIo->Media->MediaId, + 1 * BlockIo->Media->BlockSize, + BlockIo->Media->BlockSize, + (UINT8 *)PrimaryHeader + ); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "Failed to Read Partition Table Header!\n")); + FreePool (PrimaryHeader); + return EFI_DEVICE_ERROR; + } + // + // Read the partition entry. + // + EntryPtr = (UINT8 *)AllocatePool (PrimaryHeader->NumberOfPartitionEntries * PrimaryHeader->SizeOfPartitionEntry); + if (EntryPtr == NULL) { + FreePool (PrimaryHeader); + return EFI_OUT_OF_RESOURCES; + } + Status = DiskIo->ReadDisk ( + DiskIo, + BlockIo->Media->MediaId, + MultU64x32(PrimaryHeader->PartitionEntryLBA, BlockIo->Media->BlockSize), + PrimaryHeader->NumberOfPartitionEntries * PrimaryHeader->SizeOfPartitionEntry, + EntryPtr + ); + if (EFI_ERROR (Status)) { + FreePool (PrimaryHeader); + FreePool (EntryPtr); + return EFI_DEVICE_ERROR; + } + + // + // Count the valid partition + // + PartitionEntry = (EFI_PARTITION_ENTRY *)EntryPtr; + NumberOfPartition = 0; + for (Index = 0; Index < PrimaryHeader->NumberOfPartitionEntries; Index++) { + if (!CompareGuid (&PartitionEntry->PartitionTypeGUID, &mZeroGuid)) { + NumberOfPartition++; + } + PartitionEntry++; + } + + // + // Parepare Data for Measurement + // + EventSize = (UINT32)(sizeof (EFI_GPT_DATA) - sizeof (GptData->Partitions) + + NumberOfPartition * PrimaryHeader->SizeOfPartitionEntry); + TcgEvent = (TCG_PCR_EVENT *) AllocateZeroPool (EventSize + sizeof (TCG_PCR_EVENT)); + if (TcgEvent == NULL) { + FreePool (PrimaryHeader); + FreePool (EntryPtr); + return EFI_OUT_OF_RESOURCES; + } + + TcgEvent->PCRIndex = 5; + TcgEvent->EventType = EV_EFI_GPT_EVENT; + TcgEvent->EventSize = EventSize; + GptData = (EFI_GPT_DATA *) TcgEvent->Event; + + // + // Copy the EFI_PARTITION_TABLE_HEADER and NumberOfPartition + // + CopyMem ((UINT8 *)GptData, (UINT8*)PrimaryHeader, sizeof (EFI_PARTITION_TABLE_HEADER)); + GptData->NumberOfPartitions = NumberOfPartition; + // + // Copy the valid partition entry + // + PartitionEntry = (EFI_PARTITION_ENTRY*)EntryPtr; + NumberOfPartition = 0; + for (Index = 0; Index < PrimaryHeader->NumberOfPartitionEntries; Index++) { + if (!CompareGuid (&PartitionEntry->PartitionTypeGUID, &mZeroGuid)) { + CopyMem ( + (UINT8 *)&GptData->Partitions + NumberOfPartition * sizeof (EFI_PARTITION_ENTRY), + (UINT8 *)PartitionEntry, + sizeof (EFI_PARTITION_ENTRY) + ); + NumberOfPartition++; + } + PartitionEntry++; + } + + // + // Measure the GPT data + // + EventNumber = 1; + Status = TcgProtocol->HashLogExtendEvent ( + TcgProtocol, + (EFI_PHYSICAL_ADDRESS) (UINTN) (VOID *) GptData, + (UINT64) TcgEvent->EventSize, + TPM_ALG_SHA, + TcgEvent, + &EventNumber, + &EventLogLastEntry + ); + if (!EFI_ERROR (Status)) { + mMeasureGptCount++; + } + + FreePool (PrimaryHeader); + FreePool (EntryPtr); + FreePool (TcgEvent); + + return Status; +} + +/** + Measure PE image into TPM log based on the authenticode image hashing in + PE/COFF Specification 8.0 Appendix A. + + @param[in] TcgProtocol Pointer to the located TCG protocol instance. + @param[in] ImageAddress Start address of image buffer. + @param[in] ImageSize Image size + @param[in] LinkTimeBase Address that the image is loaded into memory. + @param[in] ImageType Image subsystem type. + @param[in] FilePath File path is corresponding to the input image. + + @retval EFI_SUCCESS Successfully measure image. + @retval EFI_OUT_OF_RESOURCES No enough resource to measure image. + @retval other error value +**/ +EFI_STATUS +EFIAPI +TcgMeasurePeImage ( + IN EFI_TCG_PROTOCOL *TcgProtocol, + IN EFI_PHYSICAL_ADDRESS ImageAddress, + IN UINTN ImageSize, + IN UINTN LinkTimeBase, + IN UINT16 ImageType, + IN EFI_DEVICE_PATH_PROTOCOL *FilePath + ) +{ + EFI_STATUS Status; + TCG_PCR_EVENT *TcgEvent; + EFI_IMAGE_LOAD_EVENT *ImageLoad; + UINT32 FilePathSize; + VOID *Sha1Ctx; + UINTN CtxSize; + EFI_IMAGE_DOS_HEADER *DosHdr; + UINT32 PeCoffHeaderOffset; + EFI_IMAGE_SECTION_HEADER *Section; + UINT8 *HashBase; + UINTN HashSize; + UINTN SumOfBytesHashed; + EFI_IMAGE_SECTION_HEADER *SectionHeader; + UINTN Index, Pos; + UINT16 Magic; + UINT32 EventSize; + UINT32 EventNumber; + EFI_PHYSICAL_ADDRESS EventLogLastEntry; + EFI_IMAGE_OPTIONAL_HEADER_PTR_UNION Hdr; + + Status = EFI_SUCCESS; + ImageLoad = NULL; + SectionHeader = NULL; + Sha1Ctx = NULL; + FilePathSize = (UINT32) GetDevicePathSize (FilePath); + + // + // Determine destination PCR by BootPolicy + // + EventSize = sizeof (*ImageLoad) - sizeof (ImageLoad->DevicePath) + FilePathSize; + TcgEvent = AllocateZeroPool (EventSize + sizeof (TCG_PCR_EVENT)); + if (TcgEvent == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + TcgEvent->EventSize = EventSize; + ImageLoad = (EFI_IMAGE_LOAD_EVENT *) TcgEvent->Event; + + switch (ImageType) { + case EFI_IMAGE_SUBSYSTEM_EFI_APPLICATION: + TcgEvent->EventType = EV_EFI_BOOT_SERVICES_APPLICATION; + TcgEvent->PCRIndex = 4; + break; + case EFI_IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER: + TcgEvent->EventType = EV_EFI_BOOT_SERVICES_DRIVER; + TcgEvent->PCRIndex = 2; + break; + case EFI_IMAGE_SUBSYSTEM_EFI_RUNTIME_DRIVER: + TcgEvent->EventType = EV_EFI_RUNTIME_SERVICES_DRIVER; + TcgEvent->PCRIndex = 2; + break; + default: + DEBUG (( + EFI_D_ERROR, + "TcgMeasurePeImage: Unknown subsystem type %d", + ImageType + )); + ASSERT (FALSE); + TcgEvent->EventType = ImageType; + Status = EFI_UNSUPPORTED; + goto Finish; + } + + ImageLoad->ImageLocationInMemory = ImageAddress; + ImageLoad->ImageLengthInMemory = ImageSize; + ImageLoad->ImageLinkTimeAddress = LinkTimeBase; + ImageLoad->LengthOfDevicePath = FilePathSize; + CopyMem (ImageLoad->DevicePath, FilePath, FilePathSize); + + // + // Check PE/COFF image + // + DosHdr = (EFI_IMAGE_DOS_HEADER *) (UINTN) ImageAddress; + PeCoffHeaderOffset = 0; + if (DosHdr->e_magic == EFI_IMAGE_DOS_SIGNATURE) { + PeCoffHeaderOffset = DosHdr->e_lfanew; + } + if (((EFI_TE_IMAGE_HEADER *)((UINT8 *) (UINTN) ImageAddress + PeCoffHeaderOffset))->Signature + == EFI_TE_IMAGE_HEADER_SIGNATURE) { + goto Finish; + } + + // + // PE/COFF Image Measurement + // + // NOTE: The following codes/steps are based upon the authenticode image hashing in + // PE/COFF Specification 8.0 Appendix A. + // + // + + // 1. Load the image header into memory. + + // 2. Initialize a SHA hash context. + CtxSize = Sha1GetContextSize (); + Sha1Ctx = AllocatePool (CtxSize); + if (Sha1Ctx == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto Finish; + } + + Sha1Init (Sha1Ctx); + + // + // Measuring PE/COFF Image Header; + // But CheckSum field and SECURITY data directory (certificate) are excluded + // + Hdr.Pe32 = (EFI_IMAGE_NT_HEADERS32 *)((UINT8 *) (UINTN) ImageAddress + PeCoffHeaderOffset); + Magic = Hdr.Pe32->OptionalHeader.Magic; + + // + // 3. Calculate the distance from the base of the image header to the image checksum address. + // 4. Hash the image header from its base to beginning of the image checksum. + // + HashBase = (UINT8 *) (UINTN) ImageAddress; + if (Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) { + // + // Use PE32 offset + // + HashSize = (UINTN) ((UINT8 *)(&Hdr.Pe32->OptionalHeader.CheckSum) - HashBase); + } else { + // + // Use PE32+ offset + // + HashSize = (UINTN) ((UINT8 *)(&Hdr.Pe32Plus->OptionalHeader.CheckSum) - HashBase); + } + + Sha1Update (Sha1Ctx, HashBase, HashSize); + + // + // 5. Skip over the image checksum (it occupies a single ULONG). + // 6. Get the address of the beginning of the Cert Directory. + // 7. Hash everything from the end of the checksum to the start of the Cert Directory. + // + if (Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) { + // + // Use PE32 offset + // + HashBase = (UINT8 *) &Hdr.Pe32->OptionalHeader.CheckSum + sizeof (UINT32); + HashSize = (UINTN) ((UINT8 *)(&Hdr.Pe32->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY]) - HashBase); + } else { + // + // Use PE32+ offset + // + HashBase = (UINT8 *) &Hdr.Pe32Plus->OptionalHeader.CheckSum + sizeof (UINT32); + HashSize = (UINTN) ((UINT8 *)(&Hdr.Pe32Plus->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY]) - HashBase); + } + + Sha1Update (Sha1Ctx, HashBase, HashSize); + + // + // 8. Skip over the Cert Directory. (It is sizeof(IMAGE_DATA_DIRECTORY) bytes.) + // 9. Hash everything from the end of the Cert Directory to the end of image header. + // + if (Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) { + // + // Use PE32 offset + // + HashBase = (UINT8 *) &Hdr.Pe32->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY + 1]; + HashSize = Hdr.Pe32->OptionalHeader.SizeOfHeaders - + (UINTN) ((UINT8 *)(&Hdr.Pe32->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY + 1]) - (UINT8 *) (UINTN) ImageAddress); + } else { + // + // Use PE32+ offset + // + HashBase = (UINT8 *) &Hdr.Pe32Plus->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY + 1]; + HashSize = Hdr.Pe32Plus->OptionalHeader.SizeOfHeaders - + (UINTN) ((UINT8 *)(&Hdr.Pe32Plus->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY + 1]) - (UINT8 *) (UINTN) ImageAddress); + } + + Sha1Update (Sha1Ctx, HashBase, HashSize); + + // + // 10. Set the SUM_OF_BYTES_HASHED to the size of the header + // + if (Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) { + // + // Use PE32 offset + // + SumOfBytesHashed = Hdr.Pe32->OptionalHeader.SizeOfHeaders; + } else { + // + // Use PE32+ offset + // + SumOfBytesHashed = Hdr.Pe32Plus->OptionalHeader.SizeOfHeaders; + } + + // + // 11. Build a temporary table of pointers to all the IMAGE_SECTION_HEADER + // structures in the image. The 'NumberOfSections' field of the image + // header indicates how big the table should be. Do not include any + // IMAGE_SECTION_HEADERs in the table whose 'SizeOfRawData' field is zero. + // + SectionHeader = (EFI_IMAGE_SECTION_HEADER *)AllocateZeroPool (sizeof (EFI_IMAGE_SECTION_HEADER) * Hdr.Pe32->FileHeader.NumberOfSections); + if (SectionHeader == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto Finish; + } + + // + // 12. Using the 'PointerToRawData' in the referenced section headers as + // a key, arrange the elements in the table in ascending order. In other + // words, sort the section headers according to the disk-file offset of + // the section. + // + Section = (EFI_IMAGE_SECTION_HEADER *) ( + (UINT8 *) (UINTN) ImageAddress + + PeCoffHeaderOffset + + sizeof(UINT32) + + sizeof(EFI_IMAGE_FILE_HEADER) + + Hdr.Pe32->FileHeader.SizeOfOptionalHeader + ); + for (Index = 0; Index < Hdr.Pe32->FileHeader.NumberOfSections; Index++) { + Pos = Index; + while ((Pos > 0) && (Section->PointerToRawData < SectionHeader[Pos - 1].PointerToRawData)) { + CopyMem (&SectionHeader[Pos], &SectionHeader[Pos - 1], sizeof(EFI_IMAGE_SECTION_HEADER)); + Pos--; + } + CopyMem (&SectionHeader[Pos], Section, sizeof(EFI_IMAGE_SECTION_HEADER)); + Section += 1; + } + + // + // 13. Walk through the sorted table, bring the corresponding section + // into memory, and hash the entire section (using the 'SizeOfRawData' + // field in the section header to determine the amount of data to hash). + // 14. Add the section's 'SizeOfRawData' to SUM_OF_BYTES_HASHED . + // 15. Repeat steps 13 and 14 for all the sections in the sorted table. + // + for (Index = 0; Index < Hdr.Pe32->FileHeader.NumberOfSections; Index++) { + Section = (EFI_IMAGE_SECTION_HEADER *) &SectionHeader[Index]; + if (Section->SizeOfRawData == 0) { + continue; + } + HashBase = (UINT8 *) (UINTN) ImageAddress + Section->PointerToRawData; + HashSize = (UINTN) Section->SizeOfRawData; + + Sha1Update (Sha1Ctx, HashBase, HashSize); + + SumOfBytesHashed += HashSize; + } + + // + // 16. If the file size is greater than SUM_OF_BYTES_HASHED, there is extra + // data in the file that needs to be added to the hash. This data begins + // at file offset SUM_OF_BYTES_HASHED and its length is: + // FileSize - (CertDirectory->Size) + // + if (ImageSize > SumOfBytesHashed) { + HashBase = (UINT8 *) (UINTN) ImageAddress + SumOfBytesHashed; + if (Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) { + // + // Use PE32 offset + // + HashSize = (UINTN)(ImageSize - + Hdr.Pe32->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY].Size - + SumOfBytesHashed); + } else { + // + // Use PE32+ offset + // + HashSize = (UINTN)(ImageSize - + Hdr.Pe32Plus->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY].Size - + SumOfBytesHashed); + } + + Sha1Update (Sha1Ctx, HashBase, HashSize); + } + + // + // 17. Finalize the SHA hash. + // + Sha1Final (Sha1Ctx, (UINT8 *)&TcgEvent->Digest); + + // + // Log the PE data + // + EventNumber = 1; + Status = TcgProtocol->HashLogExtendEvent ( + TcgProtocol, + (EFI_PHYSICAL_ADDRESS) (UINTN) (VOID *) NULL, + 0, + TPM_ALG_SHA, + TcgEvent, + &EventNumber, + &EventLogLastEntry + ); + +Finish: + FreePool (TcgEvent); + + if (SectionHeader != NULL) { + FreePool (SectionHeader); + } + + if (Sha1Ctx != NULL ) { + FreePool (Sha1Ctx); + } + return Status; +} + +/** + The security handler is used to abstract platform-specific policy + from the DXE core response to an attempt to use a file that returns a + given status for the authentication check from the section extraction protocol. + + The possible responses in a given SAP implementation may include locking + flash upon failure to authenticate, attestation logging for all signed drivers, + and other exception operations. The File parameter allows for possible logging + within the SAP of the driver. + + If File is NULL, then EFI_INVALID_PARAMETER is returned. + + If the file specified by File with an authentication status specified by + AuthenticationStatus is safe for the DXE Core to use, then EFI_SUCCESS is returned. + + If the file specified by File with an authentication status specified by + AuthenticationStatus is not safe for the DXE Core to use under any circumstances, + then EFI_ACCESS_DENIED is returned. + + If the file specified by File with an authentication status specified by + AuthenticationStatus is not safe for the DXE Core to use right now, but it + might be possible to use it at a future time, then EFI_SECURITY_VIOLATION is + returned. + + @param[in, out] AuthenticationStatus This is the authentication status returned + from the securitymeasurement 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. + + @retval EFI_SUCCESS The file specified by File did authenticate, and the + platform policy dictates that the DXE Core may use File. + @retval EFI_INVALID_PARAMETER File is NULL. + @retval EFI_SECURITY_VIOLATION The file specified by File did not authenticate, and + the platform policy dictates that File should be placed + in the untrusted state. A file may be promoted from + the untrusted to the trusted state at a future time + with a call to the Trust() DXE Service. + @retval EFI_ACCESS_DENIED The file specified by File did not authenticate, and + the platform policy dictates that File should not be + used for any purpose. + +**/ +EFI_STATUS +EFIAPI +DxeTpmMeasureBootHandler ( + IN OUT UINT32 AuthenticationStatus, + IN CONST EFI_DEVICE_PATH_PROTOCOL *File, + IN VOID *FileBuffer OPTIONAL, + IN UINTN FileSize OPTIONAL + ) +{ + EFI_TCG_PROTOCOL *TcgProtocol; + EFI_STATUS Status; + TCG_EFI_BOOT_SERVICE_CAPABILITY ProtocolCapability; + UINT32 TCGFeatureFlags; + EFI_PHYSICAL_ADDRESS EventLogLocation; + EFI_PHYSICAL_ADDRESS EventLogLastEntry; + EFI_DEVICE_PATH_PROTOCOL *DevicePathNode; + EFI_DEVICE_PATH_PROTOCOL *OrigDevicePathNode; + EFI_HANDLE Handle; + BOOLEAN ApplicationRequired; + PE_COFF_LOADER_IMAGE_CONTEXT ImageContext; + + if (File == NULL) { + return EFI_INVALID_PARAMETER; + } + + Status = gBS->LocateProtocol (&gEfiTcgProtocolGuid, NULL, (VOID **) &TcgProtocol); + if (EFI_ERROR (Status)) { + // + // TCG protocol is not installed. So, TPM is not present. + // Don't do any measurement, and directly return EFI_SUCCESS. + // + return EFI_SUCCESS; + } + + ProtocolCapability.Size = (UINT8) sizeof (ProtocolCapability); + Status = TcgProtocol->StatusCheck ( + TcgProtocol, + &ProtocolCapability, + &TCGFeatureFlags, + &EventLogLocation, + &EventLogLastEntry + ); + if (EFI_ERROR (Status) || ProtocolCapability.TPMDeactivatedFlag) { + // + // TPM device doesn't work or activate. + // + return EFI_SUCCESS; + } + + // + // Copy File Device Path + // + OrigDevicePathNode = DuplicateDevicePath (File); + ASSERT (OrigDevicePathNode != NULL); + + // + // 1. Check whether this device path support BlockIo protocol. + // Is so, this device path may be a GPT device path. + // + DevicePathNode = OrigDevicePathNode; + Status = gBS->LocateDevicePath (&gEfiBlockIoProtocolGuid, &DevicePathNode, &Handle); + if (!EFI_ERROR (Status) && !mMeasureGptTableFlag) { + // + // Find the gpt partion on the given devicepath + // + DevicePathNode = OrigDevicePathNode; + while (!IsDevicePathEnd (DevicePathNode)) { + // + // Find the Gpt partition + // + if (DevicePathType (DevicePathNode) == MEDIA_DEVICE_PATH && + DevicePathSubType (DevicePathNode) == MEDIA_HARDDRIVE_DP) { + // + // Check whether it is a gpt partition or not + // + if (((HARDDRIVE_DEVICE_PATH *) DevicePathNode)->MBRType == MBR_TYPE_EFI_PARTITION_TABLE_HEADER && + ((HARDDRIVE_DEVICE_PATH *) DevicePathNode)->SignatureType == SIGNATURE_TYPE_GUID) { + + // + // Change the partition device path to its parent device path (disk) and get the handle. + // + DevicePathNode->Type = END_DEVICE_PATH_TYPE; + DevicePathNode->SubType = END_ENTIRE_DEVICE_PATH_SUBTYPE; + DevicePathNode = OrigDevicePathNode; + Status = gBS->LocateDevicePath ( + &gEfiDiskIoProtocolGuid, + &DevicePathNode, + &Handle + ); + if (!EFI_ERROR (Status)) { + // + // Measure GPT disk. + // + Status = TcgMeasureGptTable (TcgProtocol, Handle); + if (!EFI_ERROR (Status)) { + // + // GPT disk check done. + // + mMeasureGptTableFlag = TRUE; + } + } + FreePool (OrigDevicePathNode); + OrigDevicePathNode = DuplicateDevicePath (File); + ASSERT (OrigDevicePathNode != NULL); + break; + } + } + DevicePathNode = NextDevicePathNode (DevicePathNode); + } + } + + // + // 2. Measure PE image. + // + ApplicationRequired = FALSE; + + // + // Check whether this device path support FV2 protocol. + // + DevicePathNode = OrigDevicePathNode; + Status = gBS->LocateDevicePath (&gEfiFirmwareVolume2ProtocolGuid, &DevicePathNode, &Handle); + if (!EFI_ERROR (Status)) { + // + // Don't check FV image, and directly return EFI_SUCCESS. + // It can be extended to the specific FV authentication according to the different requirement. + // + if (IsDevicePathEnd (DevicePathNode)) { + return EFI_SUCCESS; + } + // + // The image from Firmware image will not be mearsured. + // Current policy doesn't measure PeImage from Firmware if it is driver + // If the got PeImage is application, it will be still be measured. + // + ApplicationRequired = TRUE; + } + + // + // File is not found. + // + if (FileBuffer == NULL) { + Status = EFI_SECURITY_VIOLATION; + goto Finish; + } + + // + // Measure PE Image + // + DevicePathNode = OrigDevicePathNode; + ZeroMem (&ImageContext, sizeof (ImageContext)); + ImageContext.Handle = (VOID *) FileBuffer; + ImageContext.ImageRead = (PE_COFF_LOADER_READ_FILE) ImageRead; + + // + // Get information about the image being loaded + // + Status = PeCoffLoaderGetImageInfo (&ImageContext); + if (EFI_ERROR (Status)) { + // + // The information can't be got from the invalid PeImage + // + goto Finish; + } + + // + // Measure only application if Application flag is set + // Measure drivers and applications if Application flag is not set + // + if ((!ApplicationRequired) || + (ApplicationRequired && ImageContext.ImageType == EFI_IMAGE_SUBSYSTEM_EFI_APPLICATION)) { + // + // Print the image path to be measured. + // + DEBUG_CODE_BEGIN (); + CHAR16 *ToText; + EFI_DEVICE_PATH_TO_TEXT_PROTOCOL *DevPathToText; + Status = gBS->LocateProtocol ( + &gEfiDevicePathToTextProtocolGuid, + NULL, + (VOID **) &DevPathToText + ); + if (!EFI_ERROR (Status)) { + ToText = DevPathToText->ConvertDevicePathToText ( + DevicePathNode, + FALSE, + TRUE + ); + if (ToText != NULL) { + DEBUG ((DEBUG_INFO, "The measured image path is %s.\n", ToText)); + } + } + DEBUG_CODE_END (); + + // + // Measure PE image into TPM log. + // + Status = TcgMeasurePeImage ( + TcgProtocol, + (EFI_PHYSICAL_ADDRESS) (UINTN) FileBuffer, + FileSize, + (UINTN) ImageContext.ImageAddress, + ImageContext.ImageType, + DevicePathNode + ); + } + + // + // Done, free the allocated resource. + // +Finish: + FreePool (OrigDevicePathNode); + + return Status; +} + +/** + Register the security handler to provide TPM measure boot service. + + @param ImageHandle ImageHandle of the loaded driver. + @param SystemTable Pointer to the EFI System Table. + + @retval EFI_SUCCESS Register successfully. + @retval EFI_OUT_OF_RESOURCES No enough memory to register this handler. +**/ +EFI_STATUS +EFIAPI +DxeTpmMeasureBootLibConstructor ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + return RegisterSecurityHandler ( + DxeTpmMeasureBootHandler, + EFI_AUTH_OPERATION_MEASURE_IMAGE | EFI_AUTH_OPERATION_IMAGE_REQUIRED + ); +} diff --git a/SecurityPkg/Library/DxeTpmMeasureBootLib/DxeTpmMeasureBootLib.inf b/SecurityPkg/Library/DxeTpmMeasureBootLib/DxeTpmMeasureBootLib.inf new file mode 100644 index 0000000000..bf83bf1022 --- /dev/null +++ b/SecurityPkg/Library/DxeTpmMeasureBootLib/DxeTpmMeasureBootLib.inf @@ -0,0 +1,54 @@ +## @file +# The library instance provides security service of TPM measure boot. +# +# Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.
+# 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. +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = DxeTpmMeasureBootLib + FILE_GUID = 6C60C7D0-922A-4b7c-87D7-E503EDD73BBF + MODULE_TYPE = DXE_DRIVER + VERSION_STRING = 1.0 + LIBRARY_CLASS = NULL|DXE_DRIVER DXE_RUNTIME_DRIVER DXE_SAL_DRIVER DXE_SMM_DRIVER UEFI_APPLICATION UEFI_DRIVER + CONSTRUCTOR = DxeTpmMeasureBootLibConstructor + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = IA32 X64 IPF EBC +# + +[Sources] + DxeTpmMeasureBootLib.c + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + SecurityPkg/SecurityPkg.dec + CryptoPkg/CryptoPkg.dec + +[LibraryClasses] + BaseMemoryLib + DebugLib + MemoryAllocationLib + DevicePathLib + UefiBootServicesTableLib + BaseCryptLib + PeCoffLib + BaseLib + SecurityManagementLib + +[Protocols] + gEfiTcgProtocolGuid ## CONSUMES + gEfiFirmwareVolume2ProtocolGuid ## CONSUMES + gEfiBlockIoProtocolGuid ## CONSUMES + gEfiDiskIoProtocolGuid ## CONSUMES + gEfiDevicePathToTextProtocolGuid ## SOMETIMES_CONSUMES (Only used in debug mode) diff --git a/SecurityPkg/Library/PlatformSecureLibNull/PlatformSecureLibNull.c b/SecurityPkg/Library/PlatformSecureLibNull/PlatformSecureLibNull.c new file mode 100644 index 0000000000..f085d62b77 --- /dev/null +++ b/SecurityPkg/Library/PlatformSecureLibNull/PlatformSecureLibNull.c @@ -0,0 +1,39 @@ +/** @file + Provides a secure platform-specific method to clear PK(Platform Key). + +Copyright (c) 2011, Intel Corporation. All rights reserved.
+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. + +**/ + +/** + + This function detects whether a secure platform-specific method to clear PK(Platform Key) + is configured by platform owner. This method is provided for users force to clear PK + in case incorrect enrollment mis-haps. + + UEFI231 spec chapter 27.5.2 stipulates: The platform key may also be cleared using + a secure platform-specific method. In this case, the global variable SetupMode + must also be updated to 1. + + NOTE THAT: This function cannot depend on any EFI Variable Service since they are + not available when this function is called in AuthenticateVariable driver. + + @retval TRUE The Platform owner wants to force clear PK. + @retval FALSE The Platform owner doesn't want to force clear PK. + +**/ +BOOLEAN +EFIAPI +ForceClearPK ( + VOID + ) +{ + return FALSE; +} diff --git a/SecurityPkg/Library/PlatformSecureLibNull/PlatformSecureLibNull.inf b/SecurityPkg/Library/PlatformSecureLibNull/PlatformSecureLibNull.inf new file mode 100644 index 0000000000..5750198aa7 --- /dev/null +++ b/SecurityPkg/Library/PlatformSecureLibNull/PlatformSecureLibNull.inf @@ -0,0 +1,33 @@ +## @file +# Provides a secure platform-specific method to clear PK(Platform Key). +# +# Copyright (c) 2011, Intel Corporation. All rights reserved.
+# 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. +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = PlatformSecureLibNull + FILE_GUID = 7FA68D82-10A4-4e71-9524-D3D9500D3CDF + MODULE_TYPE = DXE_DRIVER + VERSION_STRING = 1.0 + LIBRARY_CLASS = PlatformSecureLib|DXE_RUNTIME_DRIVER DXE_SMM_DRIVER + + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = IA32 X64 IPF EBC +# + +[Sources] + PlatformSecureLibNull.c + +[Packages] + MdePkg/MdePkg.dec diff --git a/SecurityPkg/Library/TpmCommLib/CommonHeader.h b/SecurityPkg/Library/TpmCommLib/CommonHeader.h new file mode 100644 index 0000000000..b8496c7276 --- /dev/null +++ b/SecurityPkg/Library/TpmCommLib/CommonHeader.h @@ -0,0 +1,29 @@ +/** @file + The intenal header file for TpmCommLib. + +Copyright (c) 2006 - 2010, Intel Corporation. All rights reserved.
+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. + +**/ + +#ifndef _TPMCOMMLIB_COMMON_HEADER_H_ +#define _TPMCOMMLIB_COMMON_HEADER_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#endif diff --git a/SecurityPkg/Library/TpmCommLib/TisPc.c b/SecurityPkg/Library/TpmCommLib/TisPc.c new file mode 100644 index 0000000000..3d74a012df --- /dev/null +++ b/SecurityPkg/Library/TpmCommLib/TisPc.c @@ -0,0 +1,180 @@ +/** @file + Basic TIS (TPM Interface Specification) functions. + +Copyright (c) 2005 - 2011, Intel Corporation. All rights reserved.
+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 "CommonHeader.h" + +/** + Check whether TPM chip exist. + + @param[in] TisReg Pointer to TIS register. + + @retval TRUE TPM chip exists. + @retval FALSE TPM chip is not found. +**/ +BOOLEAN +TisPcPresenceCheck ( + IN TIS_PC_REGISTERS_PTR TisReg + ) +{ + UINT8 RegRead; + + RegRead = MmioRead8 ((UINTN)&TisReg->Access); + return (BOOLEAN)(RegRead != (UINT8)-1); +} + +/** + Check whether the value of a TPM chip register satisfies the input BIT setting. + + @param[in] Register Address port of register to be checked. + @param[in] BitSet Check these data bits are set. + @param[in] BitClear Check these data bits are clear. + @param[in] TimeOut The max wait time (unit MicroSecond) when checking register. + + @retval EFI_SUCCESS The register satisfies the check bit. + @retval EFI_TIMEOUT The register can't run into the expected status in time. +**/ +EFI_STATUS +EFIAPI +TisPcWaitRegisterBits ( + IN UINT8 *Register, + IN UINT8 BitSet, + IN UINT8 BitClear, + IN UINT32 TimeOut + ) +{ + UINT8 RegRead; + UINT32 WaitTime; + + for (WaitTime = 0; WaitTime < TimeOut; WaitTime += 30){ + RegRead = MmioRead8 ((UINTN)Register); + if ((RegRead & BitSet) == BitSet && (RegRead & BitClear) == 0) + return EFI_SUCCESS; + MicroSecondDelay (30); + } + return EFI_TIMEOUT; +} + +/** + Get BurstCount by reading the burstCount field of a TIS regiger + in the time of default TIS_TIMEOUT_D. + + @param[in] TisReg Pointer to TIS register. + @param[out] BurstCount Pointer to a buffer to store the got BurstConut. + + @retval EFI_SUCCESS Get BurstCount. + @retval EFI_INVALID_PARAMETER TisReg is NULL or BurstCount is NULL. + @retval EFI_TIMEOUT BurstCount can't be got in time. +**/ +EFI_STATUS +EFIAPI +TisPcReadBurstCount ( + IN TIS_PC_REGISTERS_PTR TisReg, + OUT UINT16 *BurstCount + ) +{ + UINT32 WaitTime; + UINT8 DataByte0; + UINT8 DataByte1; + + if (BurstCount == NULL || TisReg == NULL) { + return EFI_INVALID_PARAMETER; + } + + WaitTime = 0; + do { + // + // TIS_PC_REGISTERS_PTR->burstCount is UINT16, but it is not 2bytes aligned, + // so it needs to use MmioRead8 to read two times + // + DataByte0 = MmioRead8 ((UINTN)&TisReg->BurstCount); + DataByte1 = MmioRead8 ((UINTN)&TisReg->BurstCount + 1); + *BurstCount = (UINT16)((DataByte1 << 8) + DataByte0); + if (*BurstCount != 0) { + return EFI_SUCCESS; + } + MicroSecondDelay (30); + WaitTime += 30; + } while (WaitTime < TIS_TIMEOUT_D); + + return EFI_TIMEOUT; +} + +/** + Set TPM chip to ready state by sending ready command TIS_PC_STS_READY + to Status Register in time. + + @param[in] TisReg Pointer to TIS register. + + @retval EFI_SUCCESS TPM chip enters into ready state. + @retval EFI_INVALID_PARAMETER TisReg is NULL. + @retval EFI_TIMEOUT TPM chip can't be set to ready state in time. +**/ +EFI_STATUS +EFIAPI +TisPcPrepareCommand ( + IN TIS_PC_REGISTERS_PTR TisReg + ) +{ + EFI_STATUS Status; + + if (TisReg == NULL) { + return EFI_INVALID_PARAMETER; + } + + MmioWrite8((UINTN)&TisReg->Status, TIS_PC_STS_READY); + Status = TisPcWaitRegisterBits ( + &TisReg->Status, + TIS_PC_STS_READY, + 0, + TIS_TIMEOUT_B + ); + return Status; +} + +/** + Get the control of TPM chip by sending requestUse command TIS_PC_ACC_RQUUSE + to ACCESS Register in the time of default TIS_TIMEOUT_D. + + @param[in] TisReg Pointer to TIS register. + + @retval EFI_SUCCESS Get the control of TPM chip. + @retval EFI_INVALID_PARAMETER TisReg is NULL. + @retval EFI_NOT_FOUND TPM chip doesn't exit. + @retval EFI_TIMEOUT Can't get the TPM control in time. +**/ +EFI_STATUS +EFIAPI +TisPcRequestUseTpm ( + IN TIS_PC_REGISTERS_PTR TisReg + ) +{ + EFI_STATUS Status; + + if (TisReg == NULL) { + return EFI_INVALID_PARAMETER; + } + + if (!TisPcPresenceCheck (TisReg)) { + return EFI_NOT_FOUND; + } + + MmioWrite8((UINTN)&TisReg->Access, TIS_PC_ACC_RQUUSE); + Status = TisPcWaitRegisterBits ( + &TisReg->Access, + (UINT8)(TIS_PC_ACC_ACTIVE |TIS_PC_VALID), + 0, + TIS_TIMEOUT_D + ); + return Status; +} diff --git a/SecurityPkg/Library/TpmCommLib/TpmComm.c b/SecurityPkg/Library/TpmCommLib/TpmComm.c new file mode 100644 index 0000000000..3197f96a99 --- /dev/null +++ b/SecurityPkg/Library/TpmCommLib/TpmComm.c @@ -0,0 +1,50 @@ +/** @file + Basic TPM command functions. + +Copyright (c) 2005 - 2010, Intel Corporation. All rights reserved.
+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 "CommonHeader.h" + +/** + Single function calculates SHA1 digest value for all raw data. It + combines Sha1Init(), Sha1Update() and Sha1Final(). + + @param[in] Data Raw data to be digested. + @param[in] DataLen Size of the raw data. + @param[out] Digest Pointer to a buffer that stores the final digest. + + @retval EFI_SUCCESS Always successfully calculate the final digest. +**/ +EFI_STATUS +EFIAPI +TpmCommHashAll ( + IN CONST UINT8 *Data, + IN UINTN DataLen, + OUT TPM_DIGEST *Digest + ) +{ + VOID *Sha1Ctx; + UINTN CtxSize; + + CtxSize = Sha1GetContextSize (); + Sha1Ctx = AllocatePool (CtxSize); + ASSERT (Sha1Ctx != NULL); + + Sha1Init (Sha1Ctx); + Sha1Update (Sha1Ctx, Data, DataLen); + Sha1Final (Sha1Ctx, (UINT8 *)Digest); + + FreePool (Sha1Ctx); + + return EFI_SUCCESS; +} + diff --git a/SecurityPkg/Library/TpmCommLib/TpmCommLib.inf b/SecurityPkg/Library/TpmCommLib/TpmCommLib.inf new file mode 100644 index 0000000000..7188a3b165 --- /dev/null +++ b/SecurityPkg/Library/TpmCommLib/TpmCommLib.inf @@ -0,0 +1,46 @@ +## @file +# TpmCommLib instance implements basis TPM Interface Specification (TIS) and TPM command functions. +# +# Copyright (c) 2006 - 2011, Intel Corporation. All rights reserved.
+# 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. +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = TpmCommLib + FILE_GUID = 7d9fe32e-a6a9-4cdf-abff-10cc7f22e1c9 + MODULE_TYPE = PEIM + VERSION_STRING = 1.0 + LIBRARY_CLASS = TpmCommLib|DXE_DRIVER UEFI_DRIVER PEIM DXE_SMM_DRIVER + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = IA32 X64 IPF +# + +[Sources] + TisPc.c + TpmComm.c + CommonHeader.h + +[Packages] + MdePkg/MdePkg.dec + SecurityPkg/SecurityPkg.dec + CryptoPkg/CryptoPkg.dec + +[LibraryClasses] + BaseLib + BaseMemoryLib + IoLib + TimerLib + BaseCryptLib + MemoryAllocationLib + DebugLib + -- cgit v1.2.3