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:
xdu2 2011-10-28 09:54:08 +00:00
parent d26727de5f
commit 45bf2c4789
1 changed files with 207 additions and 178 deletions

View File

@ -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.