-/**\r
- Provide verification service for signed images in AuditMode, which include both signature validation\r
- and platform policy control. For signature types, both UEFI WIN_CERTIFICATE_UEFI_GUID and\r
- MSFT Authenticode type signatures are supported. \r
-\r
- In this implementation, only verify external executables when in AuditMode.\r
- Executables from FV is bypass, so pass in AuthenticationStatus is ignored. Other authentication status\r
- are record into IMAGE_EXECUTION_TABLE.\r
-\r
- The image verification policy is:\r
- If the image is signed,\r
- At least one valid signature or at least one hash value of the image must match a record\r
- in the security database "db", and no valid signature nor any hash value of the image may\r
- be reflected in the security database "dbx".\r
- Otherwise, the image is not signed,\r
- The SHA256 hash value of the image must match a record in the security database "db", and\r
- not be reflected in the security data base "dbx".\r
-\r
- Caution: This function may receive untrusted input.\r
- PE/COFF image is external input, so this function will validate its data structure\r
- within this image buffer before use.\r
-\r
- @param[in] AuthenticationStatus\r
- This is the authentication status returned from the security\r
- measurement services for the input file.\r
- @param[in] File This is a pointer to the device path of the file that is\r
- being dispatched. This will optionally be used for logging.\r
- @param[in] FileBuffer File buffer matches the input file device path.\r
- @param[in] FileSize Size of File buffer matches the input file device path.\r
- @param[in] BootPolicy A boot policy that was used to call LoadImage() UEFI service.\r
-\r
- @retval EFI_SUCCESS The authenticate info is sucessfully stored for the file \r
- specified by DevicePath and non-NULL FileBuffer \r
- @retval EFI_ACCESS_DENIED The file specified by File and FileBuffer did not\r
- authenticate, and the platform policy dictates that the DXE\r
- Foundation many not use File.\r
-\r
-**/\r
-EFI_STATUS\r
-EFIAPI\r
-ImageVerificationInAuditMode (\r
- IN UINT32 AuthenticationStatus,\r
- IN CONST EFI_DEVICE_PATH_PROTOCOL *File,\r
- IN VOID *FileBuffer,\r
- IN UINTN FileSize,\r
- IN BOOLEAN BootPolicy\r
- )\r
-{\r
- EFI_STATUS Status;\r
- UINT16 Magic;\r
- EFI_IMAGE_DOS_HEADER *DosHdr;\r
- EFI_SIGNATURE_LIST *SignatureList;\r
- EFI_IMAGE_EXECUTION_ACTION Action;\r
- WIN_CERTIFICATE *WinCertificate;\r
- UINT32 Policy;\r
- PE_COFF_LOADER_IMAGE_CONTEXT ImageContext;\r
- UINT32 NumberOfRvaAndSizes;\r
- WIN_CERTIFICATE_EFI_PKCS *PkcsCertData;\r
- WIN_CERTIFICATE_UEFI_GUID *WinCertUefiGuid;\r
- UINT8 *AuthData;\r
- UINTN AuthDataSize;\r
- EFI_IMAGE_DATA_DIRECTORY *SecDataDir;\r
- UINT32 OffSet;\r
- CHAR16 *FilePathStr;\r
- UINTN SignatureListSize;\r
-\r
- SignatureList = NULL;\r
- WinCertificate = NULL;\r
- SecDataDir = NULL;\r
- PkcsCertData = NULL;\r
- FilePathStr = NULL;\r
- Action = EFI_IMAGE_EXECUTION_AUTH_SIG_FAILED | EFI_IMAGE_EXECUTION_INITIALIZED;\r
- Status = EFI_ACCESS_DENIED;\r
-\r
-\r
- //\r
- // Check the image type and get policy setting.\r
- //\r
- switch (GetImageType (File)) {\r
-\r
- case IMAGE_FROM_FV:\r
- Policy = ALWAYS_EXECUTE;\r
- break;\r
-\r
- case IMAGE_FROM_OPTION_ROM:\r
- Policy = PcdGet32 (PcdOptionRomImageVerificationPolicy);\r
- break;\r
-\r
- case IMAGE_FROM_REMOVABLE_MEDIA:\r
- Policy = PcdGet32 (PcdRemovableMediaImageVerificationPolicy);\r
- break;\r
-\r
- case IMAGE_FROM_FIXED_MEDIA:\r
- Policy = PcdGet32 (PcdFixedMediaImageVerificationPolicy);\r
- break;\r
-\r
- default:\r
- Policy = DENY_EXECUTE_ON_SECURITY_VIOLATION;\r
- break;\r
- }\r
-\r
- //\r
- // If policy is always/never execute, return directly.\r
- //\r
- if (Policy == ALWAYS_EXECUTE) {\r
- return EFI_SUCCESS;\r
- }\r
-\r
- //\r
- // Get Image Device Path Str\r
- //\r
- FilePathStr = ConvertDevicePathToText (File, FALSE, TRUE);\r
-\r
- //\r
- // Authentication failed because of (unspecified) firmware security policy\r
- //\r
- if (Policy == NEVER_EXECUTE) {\r
- //\r
- // No signature, record FilePath/FilePathStr only\r
- //\r
- AddImageExeInfo (EFI_IMAGE_EXECUTION_POLICY_FAILED | EFI_IMAGE_EXECUTION_INITIALIZED, FilePathStr, File, NULL, 0);\r
- goto END;\r
- }\r
-\r
- //\r
- // The policy QUERY_USER_ON_SECURITY_VIOLATION and ALLOW_EXECUTE_ON_SECURITY_VIOLATION\r
- // violates the UEFI spec and has been removed.\r
- //\r
- ASSERT (Policy != QUERY_USER_ON_SECURITY_VIOLATION && Policy != ALLOW_EXECUTE_ON_SECURITY_VIOLATION);\r
- if (Policy == QUERY_USER_ON_SECURITY_VIOLATION || Policy == ALLOW_EXECUTE_ON_SECURITY_VIOLATION) {\r
- CpuDeadLoop ();\r
- }\r
-\r
- //\r
- // Read the Dos header.\r
- //\r
- if (FileBuffer == NULL) {\r
- Status = EFI_INVALID_PARAMETER;\r
- goto END;\r
- }\r
-\r
- mImageBase = (UINT8 *) FileBuffer;\r
- mImageSize = FileSize;\r
-\r
- ZeroMem (&ImageContext, sizeof (ImageContext));\r
- ImageContext.Handle = (VOID *) FileBuffer;\r
- ImageContext.ImageRead = (PE_COFF_LOADER_READ_FILE) DxeImageVerificationLibImageRead;\r
-\r
- //\r
- // Get information about the image being loaded\r
- //\r
- Status = PeCoffLoaderGetImageInfo (&ImageContext);\r
- if (EFI_ERROR (Status)) {\r
- //\r
- // The information can't be got from the invalid PeImage\r
- //\r
- goto END;\r
- }\r
-\r
-\r
- DosHdr = (EFI_IMAGE_DOS_HEADER *) mImageBase;\r
- if (DosHdr->e_magic == EFI_IMAGE_DOS_SIGNATURE) {\r
- //\r
- // DOS image header is present,\r
- // so read the PE header after the DOS image header.\r
- //\r
- mPeCoffHeaderOffset = DosHdr->e_lfanew;\r
- } else {\r
- mPeCoffHeaderOffset = 0;\r
- }\r
-\r
- //\r
- // Check PE/COFF image.\r
- //\r
- mNtHeader.Pe32 = (EFI_IMAGE_NT_HEADERS32 *) (mImageBase + mPeCoffHeaderOffset);\r
- if (mNtHeader.Pe32->Signature != EFI_IMAGE_NT_SIGNATURE) {\r
- //\r
- // It is not a valid Pe/Coff file.\r
- //\r
- Status = EFI_ACCESS_DENIED;\r
- goto END;\r
- }\r
-\r
- if (mNtHeader.Pe32->FileHeader.Machine == IMAGE_FILE_MACHINE_IA64 && mNtHeader.Pe32->OptionalHeader.Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) {\r
- //\r
- // NOTE: Some versions of Linux ELILO for Itanium have an incorrect magic value\r
- // in the PE/COFF Header. If the MachineType is Itanium(IA64) and the\r
- // Magic value in the OptionalHeader is EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC\r
- // then override the magic value to EFI_IMAGE_NT_OPTIONAL_HDR64_MAGIC\r
- //\r
- Magic = EFI_IMAGE_NT_OPTIONAL_HDR64_MAGIC;\r
- } else {\r
- //\r
- // Get the magic value from the PE/COFF Optional Header\r
- //\r
- Magic = mNtHeader.Pe32->OptionalHeader.Magic;\r
- }\r
-\r
- if (Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) {\r
- //\r
- // Use PE32 offset.\r
- //\r
- NumberOfRvaAndSizes = mNtHeader.Pe32->OptionalHeader.NumberOfRvaAndSizes;\r
- if (NumberOfRvaAndSizes > EFI_IMAGE_DIRECTORY_ENTRY_SECURITY) {\r
- SecDataDir = (EFI_IMAGE_DATA_DIRECTORY *) &mNtHeader.Pe32->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY];\r
- }\r
- } else {\r
- //\r
- // Use PE32+ offset.\r
- //\r
- NumberOfRvaAndSizes = mNtHeader.Pe32Plus->OptionalHeader.NumberOfRvaAndSizes;\r
- if (NumberOfRvaAndSizes > EFI_IMAGE_DIRECTORY_ENTRY_SECURITY) {\r
- SecDataDir = (EFI_IMAGE_DATA_DIRECTORY *) &mNtHeader.Pe32Plus->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY];\r
- }\r
- }\r
-\r
- //\r
- // Start Image Validation.\r
- //\r
- if (SecDataDir == NULL || SecDataDir->Size == 0) {\r
- //\r
- // This image is not signed. The SHA256 hash value of the image must match a record in the security database "db",\r
- // and not be reflected in the security data base "dbx".\r
- //\r
- if (!HashPeImage (HASHALG_SHA256)) {\r
- Status = EFI_ACCESS_DENIED;\r
- goto END;\r
- }\r
-\r
- //\r
- // Image Hash is in forbidden database (DBX).\r
- //\r
- if (!IsSignatureFoundInDatabase (EFI_IMAGE_SECURITY_DATABASE1, mImageDigest, &mCertType, mImageDigestSize)) {\r
- //\r
- // Image Hash is in allowed database (DB).\r
- //\r
- if (IsSignatureFoundInDatabase (EFI_IMAGE_SECURITY_DATABASE, mImageDigest, &mCertType, mImageDigestSize)) {\r
- Action = EFI_IMAGE_EXECUTION_AUTH_SIG_PASSED | EFI_IMAGE_EXECUTION_INITIALIZED;\r
- }\r
- }\r
-\r
- //\r
- // Add HASH digest for image without signature\r
- //\r
- Status = CreateSignatureList(mImageDigest, mImageDigestSize, &mCertType, &SignatureList, &SignatureListSize);\r
- if (!EFI_ERROR(Status)) {\r
- AddImageExeInfo (Action, FilePathStr, File, SignatureList, SignatureListSize);\r
- FreePool (SignatureList);\r
- }\r
- goto END;\r
- }\r
-\r
- //\r
- // Verify the signature of the image, multiple signatures are allowed as per PE/COFF Section 4.7\r
- // "Attribute Certificate Table".\r
- // The first certificate starts at offset (SecDataDir->VirtualAddress) from the start of the file.\r
- //\r
- for (OffSet = SecDataDir->VirtualAddress;\r
- OffSet < (SecDataDir->VirtualAddress + SecDataDir->Size);\r
- OffSet += (WinCertificate->dwLength + ALIGN_SIZE (WinCertificate->dwLength))) {\r
- WinCertificate = (WIN_CERTIFICATE *) (mImageBase + OffSet);\r
- if ((SecDataDir->VirtualAddress + SecDataDir->Size - OffSet) <= sizeof (WIN_CERTIFICATE) ||\r
- (SecDataDir->VirtualAddress + SecDataDir->Size - OffSet) < WinCertificate->dwLength) {\r
- break;\r
- }\r
-\r
- //\r
- // Verify the image's Authenticode signature, only DER-encoded PKCS#7 signed data is supported.\r
- //\r
- if (WinCertificate->wCertificateType == WIN_CERT_TYPE_PKCS_SIGNED_DATA) {\r
- //\r
- // The certificate is formatted as WIN_CERTIFICATE_EFI_PKCS which is described in the\r
- // Authenticode specification.\r
- //\r
- PkcsCertData = (WIN_CERTIFICATE_EFI_PKCS *) WinCertificate;\r
- if (PkcsCertData->Hdr.dwLength <= sizeof (PkcsCertData->Hdr)) {\r
- break;\r
- }\r
- AuthData = PkcsCertData->CertData;\r
- AuthDataSize = PkcsCertData->Hdr.dwLength - sizeof(PkcsCertData->Hdr);\r
- } else if (WinCertificate->wCertificateType == WIN_CERT_TYPE_EFI_GUID) {\r
- //\r
- // The certificate is formatted as WIN_CERTIFICATE_UEFI_GUID which is described in UEFI Spec.\r
- //\r
- WinCertUefiGuid = (WIN_CERTIFICATE_UEFI_GUID *) WinCertificate;\r
- if (WinCertUefiGuid->Hdr.dwLength <= OFFSET_OF(WIN_CERTIFICATE_UEFI_GUID, CertData)) {\r
- break;\r
- }\r
- if (!CompareGuid (&WinCertUefiGuid->CertType, &gEfiCertPkcs7Guid)) {\r
- continue;\r
- }\r
- AuthData = WinCertUefiGuid->CertData;\r
- AuthDataSize = WinCertUefiGuid->Hdr.dwLength - OFFSET_OF(WIN_CERTIFICATE_UEFI_GUID, CertData);\r
- } else {\r
- if (WinCertificate->dwLength < sizeof (WIN_CERTIFICATE)) {\r
- break;\r
- }\r
- continue;\r
- }\r
-\r
- Status = HashPeImageByType (AuthData, AuthDataSize);\r
- if (EFI_ERROR (Status)) {\r
- continue;\r
- }\r
-\r
- Action = EFI_IMAGE_EXECUTION_AUTH_SIG_FAILED | EFI_IMAGE_EXECUTION_INITIALIZED;\r
-\r
- //\r
- // Check the digital signature against the revoked certificate in forbidden database (dbx).\r
- // Check the digital signature against the valid certificate in allowed database (db).\r
- //\r
- if (!IsForbiddenByDbx (AuthData, AuthDataSize, TRUE, FilePathStr, File)) {\r
- IsAllowedByDb (AuthData, AuthDataSize, TRUE, FilePathStr, File);\r
- }\r
-\r
- //\r
- // Check the image's hash value.\r
- //\r
- if (!IsSignatureFoundInDatabase (EFI_IMAGE_SECURITY_DATABASE1, mImageDigest, &mCertType, mImageDigestSize)) {\r
- if (IsSignatureFoundInDatabase (EFI_IMAGE_SECURITY_DATABASE, mImageDigest, &mCertType, mImageDigestSize)) {\r
- Action = EFI_IMAGE_EXECUTION_AUTH_SIG_PASSED | EFI_IMAGE_EXECUTION_INITIALIZED; \r
- }\r
- }\r
-\r
- //\r
- // Add HASH digest for image with signature\r
- //\r
- Status = CreateSignatureList(mImageDigest, mImageDigestSize, &mCertType, &SignatureList, &SignatureListSize);\r
-\r
- if (!EFI_ERROR(Status)) {\r
- AddImageExeInfo (Action, FilePathStr, File, SignatureList, SignatureListSize);\r
- FreePool (SignatureList);\r
- } else {\r
- goto END;\r
- }\r
- }\r
-\r
-\r
- if (OffSet != (SecDataDir->VirtualAddress + SecDataDir->Size)) {\r
- //\r
- // The Size in Certificate Table or the attribute certicate table is corrupted.\r
- //\r
- Status = EFI_ACCESS_DENIED;\r
- } else {\r
- Status = EFI_SUCCESS;\r
- }\r
-\r
-END:\r
-\r
- if (FilePathStr != NULL) {\r
- FreePool(FilePathStr);\r
- FilePathStr = NULL;\r
- }\r
-\r
- return Status;\r
-}\r
-\r