mirror of https://github.com/acidanthera/audk.git
SecurityPkg: Update DxeImageVerificationLib with following changes:
1. Update to check image digest against dbx before execute it. 2. Update to support revoke certificate. 3. Update to support enroll unsigned PE image's Hash to allowed database (db). (Note: Unsigned Image's Hash is calculated in the same way with authenticode, the algorithm is assumed to be SHA256.) Signed-off-by: xdu2 Reviewed-by: tye Reviewed-by: gdong1 git-svn-id: https://edk2.svn.sourceforge.net/svnroot/edk2/trunk/edk2@12598 6f19259b-4bc3-4df7-8a09-765794883524
This commit is contained in:
parent
d26727de5f
commit
45bf2c4789
|
@ -78,7 +78,7 @@ GetImageType (
|
|||
// First check to see if File is from a Firmware Volume
|
||||
//
|
||||
DeviceHandle = NULL;
|
||||
TempDevicePath = (EFI_DEVICE_PATH_PROTOCOL *)File;
|
||||
TempDevicePath = (EFI_DEVICE_PATH_PROTOCOL *) File;
|
||||
Status = gBS->LocateDevicePath (
|
||||
&gEfiFirmwareVolume2ProtocolGuid,
|
||||
&TempDevicePath,
|
||||
|
@ -102,7 +102,7 @@ GetImageType (
|
|||
// Next check to see if File is from a Block I/O device
|
||||
//
|
||||
DeviceHandle = NULL;
|
||||
TempDevicePath = (EFI_DEVICE_PATH_PROTOCOL *)File;
|
||||
TempDevicePath = (EFI_DEVICE_PATH_PROTOCOL *) File;
|
||||
Status = gBS->LocateDevicePath (
|
||||
&gEfiBlockIoProtocolGuid,
|
||||
&TempDevicePath,
|
||||
|
@ -140,7 +140,7 @@ GetImageType (
|
|||
// the device path supports the Simple File System Protocol.
|
||||
//
|
||||
DeviceHandle = NULL;
|
||||
TempDevicePath = (EFI_DEVICE_PATH_PROTOCOL *)File;
|
||||
TempDevicePath = (EFI_DEVICE_PATH_PROTOCOL *) File;
|
||||
Status = gBS->LocateDevicePath (
|
||||
&gEfiSimpleFileSystemProtocolGuid,
|
||||
&TempDevicePath,
|
||||
|
@ -157,7 +157,7 @@ GetImageType (
|
|||
// 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;
|
||||
TempDevicePath = (EFI_DEVICE_PATH_PROTOCOL *) File;
|
||||
while (!IsDevicePathEndType (TempDevicePath)) {
|
||||
switch (DevicePathType (TempDevicePath)) {
|
||||
|
||||
|
@ -583,7 +583,7 @@ AddImageExeInfo (
|
|||
}
|
||||
|
||||
ImageExeInfoTable = NULL;
|
||||
EfiGetSystemConfigurationTable (&gEfiImageSecurityDatabaseGuid, (VOID**)&ImageExeInfoTable);
|
||||
EfiGetSystemConfigurationTable (&gEfiImageSecurityDatabaseGuid, (VOID **) &ImageExeInfoTable);
|
||||
if (ImageExeInfoTable != NULL) {
|
||||
//
|
||||
// The table has been found!
|
||||
|
@ -786,16 +786,20 @@ Done:
|
|||
}
|
||||
|
||||
/**
|
||||
Verify certificate in WIN_CERT_TYPE_PKCS_SIGNED_DATA format .
|
||||
Verify PKCS#7 SignedData using certificate found in Variable which formatted
|
||||
as EFI_SIGNATURE_LIST. The Variable may be PK, KEK, DB or DBX.
|
||||
|
||||
@retval EFI_SUCCESS Image pass verification.
|
||||
@retval EFI_SECURITY_VIOLATION Image fail verification.
|
||||
@retval EFI_OUT_OF_RESOURCE Fail to allocate memory.
|
||||
@param VariableName Name of Variable to search for Certificate.
|
||||
@param VendorGuid Variable vendor GUID.
|
||||
|
||||
@retval TRUE Image pass verification.
|
||||
@retval FALSE Image fail verification.
|
||||
|
||||
**/
|
||||
EFI_STATUS
|
||||
VerifyCertPkcsSignedData (
|
||||
VOID
|
||||
BOOLEAN
|
||||
IsPkcsSignedDataVerifiedBySignatureList (
|
||||
IN CHAR16 *VariableName,
|
||||
IN EFI_GUID *VendorGuid
|
||||
)
|
||||
{
|
||||
EFI_STATUS Status;
|
||||
|
@ -804,15 +808,13 @@ VerifyCertPkcsSignedData (
|
|||
EFI_SIGNATURE_LIST *CertList;
|
||||
EFI_SIGNATURE_DATA *Cert;
|
||||
UINTN DataSize;
|
||||
UINT8 *KekData;
|
||||
UINT8 *DbData;
|
||||
UINT8 *Data;
|
||||
UINT8 *RootCert;
|
||||
UINTN RootCertSize;
|
||||
UINTN Index;
|
||||
UINTN CertCount;
|
||||
|
||||
KekData = NULL;
|
||||
DbData = NULL;
|
||||
Data = NULL;
|
||||
CertList = NULL;
|
||||
Cert = NULL;
|
||||
RootCert = NULL;
|
||||
|
@ -820,33 +822,30 @@ VerifyCertPkcsSignedData (
|
|||
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);
|
||||
Status = gRT->GetVariable (VariableName, VendorGuid, NULL, &DataSize, NULL);
|
||||
if (Status == EFI_BUFFER_TOO_SMALL) {
|
||||
KekData = (UINT8 *)AllocateZeroPool (DataSize);
|
||||
if (KekData == NULL) {
|
||||
return EFI_OUT_OF_RESOURCES;
|
||||
Data = (UINT8 *) AllocateZeroPool (DataSize);
|
||||
if (Data == NULL) {
|
||||
return VerifyStatus;
|
||||
}
|
||||
|
||||
Status = gRT->GetVariable (EFI_KEY_EXCHANGE_KEY_NAME, &gEfiGlobalVariableGuid, NULL, &DataSize, (VOID *)KekData);
|
||||
Status = gRT->GetVariable (VariableName, VendorGuid, NULL, &DataSize, (VOID *) Data);
|
||||
if (EFI_ERROR (Status)) {
|
||||
goto Done;
|
||||
}
|
||||
|
||||
//
|
||||
// Find Cert Enrolled in KEK database to verify the signature in pkcs7 signed data.
|
||||
// Find X509 certificate in Signature List to verify the signature in pkcs7 signed data.
|
||||
//
|
||||
CertList = (EFI_SIGNATURE_LIST *) KekData;
|
||||
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
|
||||
// Iterate each Signature Data Node within this CertList for verify.
|
||||
//
|
||||
RootCert = Cert->SignatureData;
|
||||
RootCertSize = CertList->SignatureSize;
|
||||
|
@ -862,63 +861,6 @@ VerifyCertPkcsSignedData (
|
|||
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) {
|
||||
DbData = (UINT8 *)AllocateZeroPool (DataSize);
|
||||
if (DbData == NULL) {
|
||||
goto Done;
|
||||
}
|
||||
|
||||
Status = gRT->GetVariable (EFI_IMAGE_SECURITY_DATABASE, &gEfiImageSecurityDatabaseGuid, NULL, &DataSize, (VOID *)DbData);
|
||||
if (EFI_ERROR (Status)) {
|
||||
goto Done;
|
||||
}
|
||||
|
||||
//
|
||||
// Find Cert Enrolled in DB database to verify the signature in pkcs7 signed data.
|
||||
//
|
||||
CertList = (EFI_SIGNATURE_LIST *) DbData;
|
||||
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;
|
||||
}
|
||||
|
@ -931,15 +873,47 @@ VerifyCertPkcsSignedData (
|
|||
}
|
||||
|
||||
Done:
|
||||
if (KekData != NULL) {
|
||||
FreePool (KekData);
|
||||
if (Data != NULL) {
|
||||
FreePool (Data);
|
||||
}
|
||||
|
||||
if (DbData != NULL) {
|
||||
FreePool (DbData);
|
||||
return VerifyStatus;
|
||||
}
|
||||
|
||||
/**
|
||||
Verify certificate in WIN_CERT_TYPE_PKCS_SIGNED_DATA format.
|
||||
|
||||
@retval EFI_SUCCESS Image pass verification.
|
||||
@retval EFI_SECURITY_VIOLATION Image fail verification.
|
||||
|
||||
**/
|
||||
EFI_STATUS
|
||||
VerifyCertPkcsSignedData (
|
||||
VOID
|
||||
)
|
||||
{
|
||||
//
|
||||
// 1: Find certificate from DBX forbidden database for revoked certificate.
|
||||
//
|
||||
if (IsPkcsSignedDataVerifiedBySignatureList (EFI_IMAGE_SECURITY_DATABASE1, &gEfiImageSecurityDatabaseGuid)) {
|
||||
//
|
||||
// DBX is forbidden database, if Authenticode verification pass with
|
||||
// one of the certificate in DBX, this image should be rejected.
|
||||
//
|
||||
return EFI_SECURITY_VIOLATION;
|
||||
}
|
||||
|
||||
if (VerifyStatus) {
|
||||
//
|
||||
// 2: Find certificate from KEK database and try to verify authenticode struct.
|
||||
//
|
||||
if (IsPkcsSignedDataVerifiedBySignatureList (EFI_KEY_EXCHANGE_KEY_NAME, &gEfiGlobalVariableGuid)) {
|
||||
return EFI_SUCCESS;
|
||||
}
|
||||
|
||||
//
|
||||
// 3: Find certificate from DB database and try to verify authenticode struct.
|
||||
//
|
||||
if (IsPkcsSignedDataVerifiedBySignatureList (EFI_IMAGE_SECURITY_DATABASE, &gEfiImageSecurityDatabaseGuid)) {
|
||||
return EFI_SUCCESS;
|
||||
} else {
|
||||
return EFI_SECURITY_VIOLATION;
|
||||
|
@ -1087,6 +1061,24 @@ Done:
|
|||
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 process is:
|
||||
Is the Image signed?
|
||||
If yes,
|
||||
Does the image verify against a certificate (root or intermediate) in the allowed db?
|
||||
Run it
|
||||
Image verification fail
|
||||
Is the Image's Hash not in forbidden database and the Image's Hash in allowed db?
|
||||
Run it
|
||||
If no,
|
||||
Is the Image's Hash in the forbidden database (DBX)?
|
||||
if yes,
|
||||
Error out
|
||||
Is the Image's Hash in the allowed database (DB)?
|
||||
If yes,
|
||||
Run it
|
||||
If no,
|
||||
Error out
|
||||
|
||||
@param[in] AuthenticationStatus
|
||||
This is the authentication status returned from the security
|
||||
measurement services for the input file.
|
||||
|
@ -1216,7 +1208,7 @@ DxeImageVerificationHandler (
|
|||
}
|
||||
mImageBase = (UINT8 *) FileBuffer;
|
||||
mImageSize = FileSize;
|
||||
DosHdr = (EFI_IMAGE_DOS_HEADER *) (mImageBase);
|
||||
DosHdr = (EFI_IMAGE_DOS_HEADER *) mImageBase;
|
||||
if (DosHdr->e_magic == EFI_IMAGE_DOS_SIGNATURE) {
|
||||
//
|
||||
// DOS image header is present,
|
||||
|
@ -1242,12 +1234,12 @@ DxeImageVerificationHandler (
|
|||
//
|
||||
// Use PE32 offset.
|
||||
//
|
||||
mSecDataDir = (EFI_IMAGE_DATA_DIRECTORY *)&mNtHeader.Pe32->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY];
|
||||
mSecDataDir = (EFI_IMAGE_DATA_DIRECTORY *) &mNtHeader.Pe32->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY];
|
||||
} else if (Magic == EFI_IMAGE_NT_OPTIONAL_HDR64_MAGIC) {
|
||||
//
|
||||
// Use PE32+ offset.
|
||||
//
|
||||
mSecDataDir = (EFI_IMAGE_DATA_DIRECTORY *)&mNtHeader.Pe32Plus->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY];
|
||||
mSecDataDir = (EFI_IMAGE_DATA_DIRECTORY *) &mNtHeader.Pe32Plus->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY];
|
||||
} else {
|
||||
//
|
||||
// Invalid header magic number.
|
||||
|
@ -1268,10 +1260,34 @@ DxeImageVerificationHandler (
|
|||
//
|
||||
// This image is not signed.
|
||||
//
|
||||
if (!HashPeImage (HASHALG_SHA256)) {
|
||||
goto Done;
|
||||
}
|
||||
|
||||
if (IsSignatureFoundInDatabase (EFI_IMAGE_SECURITY_DATABASE1, mImageDigest, &mCertType, mImageDigestSize)) {
|
||||
//
|
||||
// Image Hash is in forbidden database (DBX).
|
||||
//
|
||||
Action = EFI_IMAGE_EXECUTION_AUTH_UNTESTED;
|
||||
Status = EFI_ACCESS_DENIED;
|
||||
goto Done;
|
||||
}
|
||||
|
||||
if (IsSignatureFoundInDatabase (EFI_IMAGE_SECURITY_DATABASE, mImageDigest, &mCertType, mImageDigestSize)) {
|
||||
//
|
||||
// Image Hash is in allowed database (DB).
|
||||
//
|
||||
return EFI_SUCCESS;
|
||||
}
|
||||
|
||||
//
|
||||
// Image Hash is not found in both forbidden and allowed database.
|
||||
//
|
||||
Action = EFI_IMAGE_EXECUTION_AUTH_UNTESTED;
|
||||
Status = EFI_ACCESS_DENIED;
|
||||
goto Done;
|
||||
}
|
||||
|
||||
//
|
||||
// Verify signature of executables.
|
||||
//
|
||||
|
@ -1295,7 +1311,7 @@ DxeImageVerificationHandler (
|
|||
// Verify Pkcs signed data type.
|
||||
//
|
||||
Status = HashPeImageByType();
|
||||
if (EFI_ERROR(Status)) {
|
||||
if (EFI_ERROR (Status)) {
|
||||
goto Done;
|
||||
}
|
||||
|
||||
|
@ -1306,11 +1322,15 @@ DxeImageVerificationHandler (
|
|||
// no need to check image's hash in the allowed database.
|
||||
//
|
||||
if (!EFI_ERROR (VerifyStatus)) {
|
||||
if (!IsSignatureFoundInDatabase (EFI_IMAGE_SECURITY_DATABASE1, mImageDigest, &mCertType, mImageDigestSize)) {
|
||||
return EFI_SUCCESS;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
return EFI_ACCESS_DENIED;
|
||||
Status = EFI_ACCESS_DENIED;
|
||||
goto Done;
|
||||
}
|
||||
//
|
||||
// Get image hash value as executable's signature.
|
||||
|
@ -1334,8 +1354,17 @@ DxeImageVerificationHandler (
|
|||
//
|
||||
// Verification failure.
|
||||
//
|
||||
if (!IsSignatureFoundInDatabase (EFI_IMAGE_SECURITY_DATABASE1, mImageDigest, &mCertType, mImageDigestSize) &&
|
||||
IsSignatureFoundInDatabase (EFI_IMAGE_SECURITY_DATABASE, mImageDigest, &mCertType, mImageDigestSize)) {
|
||||
//
|
||||
// Verification fail, Image Hash is not in forbidden database (DBX),
|
||||
// and Image Hash is in allowed database (DB).
|
||||
//
|
||||
Status = EFI_SUCCESS;
|
||||
} else {
|
||||
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.
|
||||
|
|
Loading…
Reference in New Issue