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

View File

@ -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. @param VariableName Name of Variable to search for Certificate.
@retval EFI_SECURITY_VIOLATION Image fail verification. @param VendorGuid Variable vendor GUID.
@retval EFI_OUT_OF_RESOURCE Fail to allocate memory.
@retval TRUE Image pass verification.
@retval FALSE Image fail verification.
**/ **/
EFI_STATUS BOOLEAN
VerifyCertPkcsSignedData ( IsPkcsSignedDataVerifiedBySignatureList (
VOID IN CHAR16 *VariableName,
IN EFI_GUID *VendorGuid
) )
{ {
EFI_STATUS Status; EFI_STATUS Status;
@ -804,15 +808,13 @@ VerifyCertPkcsSignedData (
EFI_SIGNATURE_LIST *CertList; EFI_SIGNATURE_LIST *CertList;
EFI_SIGNATURE_DATA *Cert; EFI_SIGNATURE_DATA *Cert;
UINTN DataSize; UINTN DataSize;
UINT8 *KekData; UINT8 *Data;
UINT8 *DbData;
UINT8 *RootCert; UINT8 *RootCert;
UINTN RootCertSize; UINTN RootCertSize;
UINTN Index; UINTN Index;
UINTN CertCount; UINTN CertCount;
KekData = NULL; Data = NULL;
DbData = NULL;
CertList = NULL; CertList = NULL;
Cert = NULL; Cert = NULL;
RootCert = NULL; RootCert = NULL;
@ -820,33 +822,30 @@ VerifyCertPkcsSignedData (
VerifyStatus = FALSE; VerifyStatus = FALSE;
PkcsCertData = (WIN_CERTIFICATE_EFI_PKCS *) (mImageBase + mSecDataDir->VirtualAddress); PkcsCertData = (WIN_CERTIFICATE_EFI_PKCS *) (mImageBase + mSecDataDir->VirtualAddress);
//
// 1: Find certificate from KEK database and try to verify authenticode struct.
//
DataSize = 0; 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) { if (Status == EFI_BUFFER_TOO_SMALL) {
KekData = (UINT8 *)AllocateZeroPool (DataSize); Data = (UINT8 *) AllocateZeroPool (DataSize);
if (KekData == NULL) { if (Data == NULL) {
return EFI_OUT_OF_RESOURCES; 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)) { if (EFI_ERROR (Status)) {
goto Done; 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)) { while ((DataSize > 0) && (DataSize >= CertList->SignatureListSize)) {
if (CompareGuid (&CertList->SignatureType, &gEfiCertX509Guid)) { if (CompareGuid (&CertList->SignatureType, &gEfiCertX509Guid)) {
Cert = (EFI_SIGNATURE_DATA *) ((UINT8 *) CertList + sizeof (EFI_SIGNATURE_LIST) + CertList->SignatureHeaderSize); Cert = (EFI_SIGNATURE_DATA *) ((UINT8 *) CertList + sizeof (EFI_SIGNATURE_LIST) + CertList->SignatureHeaderSize);
CertCount = (CertList->SignatureListSize - sizeof (EFI_SIGNATURE_LIST) - CertList->SignatureHeaderSize) / CertList->SignatureSize; CertCount = (CertList->SignatureListSize - sizeof (EFI_SIGNATURE_LIST) - CertList->SignatureHeaderSize) / CertList->SignatureSize;
for (Index = 0; Index < CertCount; Index++) { 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; RootCert = Cert->SignatureData;
RootCertSize = CertList->SignatureSize; RootCertSize = CertList->SignatureSize;
@ -862,63 +861,6 @@ VerifyCertPkcsSignedData (
mImageDigest, mImageDigest,
mImageDigestSize 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) { if (VerifyStatus) {
goto Done; goto Done;
} }
@ -931,15 +873,47 @@ VerifyCertPkcsSignedData (
} }
Done: Done:
if (KekData != NULL) { if (Data != NULL) {
FreePool (KekData); FreePool (Data);
} }
if (DbData != NULL) { return VerifyStatus;
FreePool (DbData);
} }
if (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;
}
//
// 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; return EFI_SUCCESS;
} else { } else {
return EFI_SECURITY_VIOLATION; return EFI_SECURITY_VIOLATION;
@ -1087,6 +1061,24 @@ Done:
In this implementation, only verify external executables when in USER MODE. In this implementation, only verify external executables when in USER MODE.
Executables from FV is bypass, so pass in AuthenticationStatus is ignored. 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 @param[in] AuthenticationStatus
This is the authentication status returned from the security This is the authentication status returned from the security
measurement services for the input file. measurement services for the input file.
@ -1216,7 +1208,7 @@ DxeImageVerificationHandler (
} }
mImageBase = (UINT8 *) FileBuffer; mImageBase = (UINT8 *) FileBuffer;
mImageSize = FileSize; mImageSize = FileSize;
DosHdr = (EFI_IMAGE_DOS_HEADER *) (mImageBase); DosHdr = (EFI_IMAGE_DOS_HEADER *) mImageBase;
if (DosHdr->e_magic == EFI_IMAGE_DOS_SIGNATURE) { if (DosHdr->e_magic == EFI_IMAGE_DOS_SIGNATURE) {
// //
// DOS image header is present, // DOS image header is present,
@ -1268,10 +1260,34 @@ DxeImageVerificationHandler (
// //
// This image is not signed. // 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; Action = EFI_IMAGE_EXECUTION_AUTH_UNTESTED;
Status = EFI_ACCESS_DENIED; Status = EFI_ACCESS_DENIED;
goto Done; 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. // Verify signature of executables.
// //
@ -1306,11 +1322,15 @@ DxeImageVerificationHandler (
// no need to check image's hash in the allowed database. // no need to check image's hash in the allowed database.
// //
if (!EFI_ERROR (VerifyStatus)) { if (!EFI_ERROR (VerifyStatus)) {
if (!IsSignatureFoundInDatabase (EFI_IMAGE_SECURITY_DATABASE1, mImageDigest, &mCertType, mImageDigestSize)) {
return EFI_SUCCESS; return EFI_SUCCESS;
} }
}
break;
default: default:
return EFI_ACCESS_DENIED; Status = EFI_ACCESS_DENIED;
goto Done;
} }
// //
// Get image hash value as executable's signature. // Get image hash value as executable's signature.
@ -1334,8 +1354,17 @@ DxeImageVerificationHandler (
// //
// Verification failure. // 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; Action = EFI_IMAGE_EXECUTION_AUTH_SIG_FAILED;
Status = EFI_ACCESS_DENIED; Status = EFI_ACCESS_DENIED;
}
} else if (IsSignatureFoundInDatabase (EFI_IMAGE_SECURITY_DATABASE1, Signature->SignatureData, &mCertType, mImageDigestSize)) { } else if (IsSignatureFoundInDatabase (EFI_IMAGE_SECURITY_DATABASE1, Signature->SignatureData, &mCertType, mImageDigestSize)) {
// //
// Executable signature verification passes, but is found in forbidden signature database. // Executable signature verification passes, but is found in forbidden signature database.