diff --git a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.c b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.c index 23e25a4b75..5ac6dda5c5 100644 --- a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.c +++ b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.c @@ -1704,6 +1704,13 @@ DxeImageVerificationHandler ( IsFound = FALSE; IsFoundInDatabase = FALSE; + // + // Sanity check + // + if (File == NULL) { + return EFI_INVALID_PARAMETER; + } + // // Check the image type and get policy setting. // diff --git a/SecurityPkg/Library/DxeImageVerificationLib/GoogleTest/DxeImageVerificationLibGoogleTest.cpp b/SecurityPkg/Library/DxeImageVerificationLib/GoogleTest/DxeImageVerificationLibGoogleTest.cpp new file mode 100644 index 0000000000..325c3f5007 --- /dev/null +++ b/SecurityPkg/Library/DxeImageVerificationLib/GoogleTest/DxeImageVerificationLibGoogleTest.cpp @@ -0,0 +1,137 @@ +/** @file + Unit tests for the implementation of DxeImageVerificationLib. + + Copyright (c) 2025, Yandex. All rights reserved. + SPDX-License-Identifier: BSD-2-Clause-Patent +**/ + +#include +#include +#include +#include +#include + +extern "C" { + #include + #include + #include + + #include "DxeImageVerificationLibGoogleTest.h" +} + +////////////////////////////////////////////////////////////////////////////// +class CheckImageTypeResult : public ::testing::Test { +public: + EFI_DEVICE_PATH_PROTOCOL File; + +protected: + MockUefiRuntimeServicesTableLib RtServicesMock; + MockUefiBootServicesTableLib BsMock; + MockDevicePathLib DevicePathMock; + + EFI_STATUS Status; + + UINT32 AuthenticationStatus; + VOID *FileBuffer; + UINTN FileSize; + BOOLEAN BootPolicy; + + virtual void + SetUp ( + ) + { + AuthenticationStatus = 0; + FileBuffer = NULL; + FileSize = 0; + BootPolicy = FALSE; + } +}; + +TEST_F (CheckImageTypeResult, ImageTypeVerifySanity) { + // Sanity check + Status = DxeImageVerificationHandler (AuthenticationStatus, NULL, FileBuffer, FileSize, BootPolicy); + EXPECT_EQ (Status, EFI_INVALID_PARAMETER); +} + +TEST_F (CheckImageTypeResult, ImageTypeVerifyImageFromFv) { + EXPECT_CALL (BsMock, gBS_LocateDevicePath) + .WillRepeatedly (testing::Return (EFI_SUCCESS)); + EXPECT_CALL (BsMock, gBS_OpenProtocol) + .WillRepeatedly (testing::Return (EFI_SUCCESS)); + + Status = DxeImageVerificationHandler (AuthenticationStatus, &File, FileBuffer, FileSize, BootPolicy); + EXPECT_EQ (Status, EFI_SUCCESS); +} + +TEST_F (CheckImageTypeResult, ImageTypeVerifyImageFromOptionRom) { + auto TestFunc = [&](EFI_STATUS ExpectedStatus) { + EXPECT_CALL (BsMock, gBS_LocateDevicePath) + .Times (3) + .WillRepeatedly (testing::Return (EFI_NOT_FOUND)); + EXPECT_CALL (BsMock, gBS_OpenProtocol) + .WillRepeatedly (testing::Return (EFI_NOT_FOUND)); + EXPECT_CALL (DevicePathMock, IsDevicePathEndType) + .WillOnce (testing::Return ((BOOLEAN)FALSE)); + EXPECT_CALL (DevicePathMock, DevicePathType) + .WillOnce (testing::Return ((UINT8)MEDIA_DEVICE_PATH)); + EXPECT_CALL (DevicePathMock, DevicePathSubType) + .WillOnce (testing::Return ((UINT8)MEDIA_RELATIVE_OFFSET_RANGE_DP)); + + Status = DxeImageVerificationHandler (AuthenticationStatus, &File, FileBuffer, FileSize, BootPolicy); + EXPECT_EQ (Status, ExpectedStatus); + }; + + PatchPcdSet32 (PcdOptionRomImageVerificationPolicy, ALWAYS_EXECUTE); + TestFunc (EFI_SUCCESS); + PatchPcdSet32 (PcdOptionRomImageVerificationPolicy, NEVER_EXECUTE); + TestFunc (EFI_ACCESS_DENIED); +} + +TEST_F (CheckImageTypeResult, ImageTypeVerifyImageFromRemovableMedia) { + auto TestFunc = [&](EFI_STATUS ExpectedStatus) { + EXPECT_CALL (BsMock, gBS_LocateDevicePath) + .Times (3) + .WillRepeatedly (testing::Return (EFI_NOT_FOUND)); + EXPECT_CALL (DevicePathMock, IsDevicePathEndType) + .WillOnce (testing::Return ((BOOLEAN)FALSE)); + EXPECT_CALL (DevicePathMock, DevicePathType) + .WillOnce (testing::Return ((UINT8)MESSAGING_DEVICE_PATH)); + EXPECT_CALL (DevicePathMock, DevicePathSubType) + .WillOnce (testing::Return ((UINT8)MSG_MAC_ADDR_DP)); + + Status = DxeImageVerificationHandler (AuthenticationStatus, &File, FileBuffer, FileSize, BootPolicy); + EXPECT_EQ (Status, ExpectedStatus); + }; + + PatchPcdSet32 (PcdRemovableMediaImageVerificationPolicy, ALWAYS_EXECUTE); + TestFunc (EFI_SUCCESS); + PatchPcdSet32 (PcdRemovableMediaImageVerificationPolicy, NEVER_EXECUTE); + TestFunc (EFI_ACCESS_DENIED); +} + +TEST_F (CheckImageTypeResult, ImageTypeVerifyImageFromFixedMedia) { + auto TestFunc = [&](EFI_STATUS ExpectedStatus) { + EXPECT_CALL (BsMock, gBS_LocateDevicePath) + .WillOnce (testing::Return (EFI_NOT_FOUND)) + .WillOnce (testing::Return (EFI_NOT_FOUND)) + .WillOnce (testing::Return (EFI_SUCCESS)); + + Status = DxeImageVerificationHandler (AuthenticationStatus, &File, FileBuffer, FileSize, BootPolicy); + EXPECT_EQ (Status, ExpectedStatus); + }; + + PatchPcdSet32 (PcdFixedMediaImageVerificationPolicy, ALWAYS_EXECUTE); + TestFunc (EFI_SUCCESS); + PatchPcdSet32 (PcdFixedMediaImageVerificationPolicy, NEVER_EXECUTE); + TestFunc (EFI_ACCESS_DENIED); +} + +int +main ( + int argc, + char *argv[] + ) +{ + testing::InitGoogleTest (&argc, argv); + return RUN_ALL_TESTS (); +} diff --git a/SecurityPkg/Library/DxeImageVerificationLib/GoogleTest/DxeImageVerificationLibGoogleTest.h b/SecurityPkg/Library/DxeImageVerificationLib/GoogleTest/DxeImageVerificationLibGoogleTest.h new file mode 100644 index 0000000000..fcaa0595f4 --- /dev/null +++ b/SecurityPkg/Library/DxeImageVerificationLib/GoogleTest/DxeImageVerificationLibGoogleTest.h @@ -0,0 +1,79 @@ +/** @file + Unit tests for the implementation of DxeImageVerificationLib. + + Copyright (c) 2025, Yandex. All rights reserved. + SPDX-License-Identifier: BSD-2-Clause-Patent +**/ + +#ifndef DXE_IMAGE_VERIFICATION_LIB_GOOGLE_TEST_H +#define DXE_IMAGE_VERIFICATION_LIB_GOOGLE_TEST_H + +/** + 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. + + The image verification policy is: + If the image is signed, + At least one valid signature or at least one hash value of the image must match a record + in the security database "db", and no valid signature nor any hash value of the image may + be reflected in the security database "dbx". + Otherwise, the image is not signed, + The hash value of the image must match a record in the security database "db", and + not be reflected in the security data base "dbx". + + Caution: This function may receive untrusted input. + PE/COFF image is external input, so this function will validate its data structure + within this image buffer before use. + + @param[in] AuthenticationStatus + This is the authentication status returned from the security + measurement services for the input file. + @param[in] File This is a pointer to the device path of the file that is + being dispatched. This will optionally be used for logging. + @param[in] FileBuffer File buffer matches the input file device path. + @param[in] FileSize Size of File buffer matches the input file device path. + @param[in] BootPolicy A boot policy that was used to call LoadImage() UEFI service. + + @retval EFI_SUCCESS The file specified by DevicePath and non-NULL + FileBuffer did authenticate, and the platform policy dictates + that the DXE Foundation may use the file. + @retval EFI_SUCCESS The device path specified by NULL device path DevicePath + and non-NULL FileBuffer did authenticate, and the platform + policy dictates that the DXE Foundation may execute the image in + FileBuffer. + @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. The image has been added to the file + execution table. + @retval EFI_ACCESS_DENIED The file specified by File and FileBuffer did not + authenticate, and the platform policy dictates that the DXE + Foundation may not use File. The image has + been added to the file execution table. + +**/ +EFI_STATUS +EFIAPI +DxeImageVerificationHandler ( + IN UINT32 AuthenticationStatus, + IN CONST EFI_DEVICE_PATH_PROTOCOL *File OPTIONAL, + IN VOID *FileBuffer, + IN UINTN FileSize, + IN BOOLEAN BootPolicy + ); + +// +// The DxeImageVerificationLib.h file has dependencies on Pi/PiFirmwareVolume.h and Pi/PiFirmwareFile.h. +// These macros are copied from the header file to prevent PiPei.h from being included in HOST_APPLICATION. +// + +// +// Authorization policy bit definition +// +#define ALWAYS_EXECUTE 0x00000000 +#define NEVER_EXECUTE 0x00000001 + +#endif // DXE_IMAGE_VERIFICATION_LIB_GOOGLE_TEST_H diff --git a/SecurityPkg/Library/DxeImageVerificationLib/GoogleTest/DxeImageVerificationLibGoogleTest.inf b/SecurityPkg/Library/DxeImageVerificationLib/GoogleTest/DxeImageVerificationLibGoogleTest.inf new file mode 100644 index 0000000000..78314b57d5 --- /dev/null +++ b/SecurityPkg/Library/DxeImageVerificationLib/GoogleTest/DxeImageVerificationLibGoogleTest.inf @@ -0,0 +1,79 @@ +## @file +# Unit test suite for the DxeImageVerificationLib using Google Test +# +# Copyright (c) 2025, Yandex. All rights reserved. +# SPDX-License-Identifier: BSD-2-Clause-Patent +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = DxeImageVerificationLibGoogleTest + FILE_GUID = 18723239-55AA-4814-9B7A-874BAF719A65 + MODULE_TYPE = HOST_APPLICATION + VERSION_STRING = 1.0 + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = IA32 X64 +# + +[Sources] + DxeImageVerificationLibGoogleTest.cpp + ../../../../MdePkg/Test/Mock/Library/GoogleTest/Protocol/MockRng.cpp + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + SecurityPkg/SecurityPkg.dec + UnitTestFrameworkPkg/UnitTestFrameworkPkg.dec + CryptoPkg/CryptoPkg.dec + +[LibraryClasses] + DxeImageVerificationLib + GoogleTestLib + BaseCryptLib + DebugLib + +[Guids] + ## SOMETIMES_CONSUMES ## Variable:L"SecureBoot" + gEfiGlobalVariableGuid + + ## SOMETIMES_CONSUMES ## Variable:L"DB" + ## SOMETIMES_CONSUMES ## Variable:L"DBX" + ## SOMETIMES_CONSUMES ## Variable:L"DBT" + ## PRODUCES ## SystemTable + ## CONSUMES ## SystemTable + gEfiImageSecurityDatabaseGuid + + ## SOMETIMES_CONSUMES ## GUID # Unique ID for the type of the signature. + ## SOMETIMES_PRODUCES ## GUID # Unique ID for the type of the signature. + gEfiCertSha1Guid + + ## SOMETIMES_CONSUMES ## GUID # Unique ID for the type of the signature. + ## SOMETIMES_PRODUCES ## GUID # Unique ID for the type of the signature. + gEfiCertSha256Guid + + ## SOMETIMES_CONSUMES ## GUID # Unique ID for the type of the signature. + ## SOMETIMES_PRODUCES ## GUID # Unique ID for the type of the signature. + gEfiCertSha384Guid + + ## SOMETIMES_CONSUMES ## GUID # Unique ID for the type of the signature. + ## SOMETIMES_PRODUCES ## GUID # Unique ID for the type of the signature. + gEfiCertSha512Guid + + gEfiCertX509Guid ## SOMETIMES_CONSUMES ## GUID # Unique ID for the type of the signature. + gEfiCertX509Sha256Guid ## SOMETIMES_CONSUMES ## GUID # Unique ID for the type of the signature. + gEfiCertX509Sha384Guid ## SOMETIMES_CONSUMES ## GUID # Unique ID for the type of the signature. + gEfiCertX509Sha512Guid ## SOMETIMES_CONSUMES ## GUID # Unique ID for the type of the signature. + gEfiCertPkcs7Guid ## SOMETIMES_CONSUMES ## GUID # Unique ID for the type of the certificate. + +[Protocols] + gEfiFirmwareVolume2ProtocolGuid ## SOMETIMES_CONSUMES + gEfiBlockIoProtocolGuid ## SOMETIMES_CONSUMES + gEfiSimpleFileSystemProtocolGuid ## SOMETIMES_CONSUMES + +[Pcd] + gEfiSecurityPkgTokenSpaceGuid.PcdOptionRomImageVerificationPolicy ## SOMETIMES_CONSUMES + gEfiSecurityPkgTokenSpaceGuid.PcdRemovableMediaImageVerificationPolicy ## SOMETIMES_CONSUMES + gEfiSecurityPkgTokenSpaceGuid.PcdFixedMediaImageVerificationPolicy ## SOMETIMES_CONSUMES diff --git a/SecurityPkg/Test/SecurityPkgHostTest.dsc b/SecurityPkg/Test/SecurityPkgHostTest.dsc index 1655e573ea..7159b20b6b 100644 --- a/SecurityPkg/Test/SecurityPkgHostTest.dsc +++ b/SecurityPkg/Test/SecurityPkgHostTest.dsc @@ -46,3 +46,23 @@ PlatformPKProtectionLib|SecurityPkg/Test/Mock/Library/GoogleTest/MockPlatformPKProtectionLib/MockPlatformPKProtectionLib.inf UefiLib|MdePkg/Test/Mock/Library/GoogleTest/MockUefiLib/MockUefiLib.inf } + SecurityPkg/Library/DxeImageVerificationLib/GoogleTest/DxeImageVerificationLibGoogleTest.inf { + + UefiRuntimeServicesTableLib|MdePkg/Test/Mock/Library/GoogleTest/MockUefiRuntimeServicesTableLib/MockUefiRuntimeServicesTableLib.inf + UefiBootServicesTableLib|MdePkg/Test/Mock/Library/GoogleTest/MockUefiBootServicesTableLib/MockUefiBootServicesTableLib.inf + DxeImageVerificationLib|SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.inf + BaseCryptLib|CryptoPkg/Library/BaseCryptLib/UnitTestHostBaseCryptLib.inf + OpensslLib|CryptoPkg/Library/OpensslLib/OpensslLibFull.inf + RngLib|MdePkg/Library/BaseRngLib/BaseRngLib.inf + UefiLib|MdePkg/Test/Mock/Library/GoogleTest/MockUefiLib/MockUefiLib.inf + DevicePathLib|MdePkg/Test/Mock/Library/GoogleTest/MockDevicePathLib/MockDevicePathLib.inf + SecurityManagementLib|MdeModulePkg/Test/Mock/Library/GoogleTest/MockSecurityManagementLib/MockSecurityManagementLib.inf + PeCoffLib|MdePkg/Library/BasePeCoffLib/BasePeCoffLib.inf + PeCoffExtraActionLib|MdePkg/Library/BasePeCoffExtraActionLibNull/BasePeCoffExtraActionLibNull.inf + TpmMeasurementLib|MdeModulePkg/Library/TpmMeasurementLibNull/TpmMeasurementLibNull.inf + } + +[PcdsPatchableInModule] + gEfiSecurityPkgTokenSpaceGuid.PcdOptionRomImageVerificationPolicy|0x04 + gEfiSecurityPkgTokenSpaceGuid.PcdRemovableMediaImageVerificationPolicy|0x04 + gEfiSecurityPkgTokenSpaceGuid.PcdFixedMediaImageVerificationPolicy|0x04