/* Copyright (c) 2017-2019, The Linux Foundation. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following * disclaimer in the documentation and/or other materials provided * with the distribution. * * Neither the name of The Linux Foundation nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "VerifiedBoot.h" #include "BootLinux.h" #include "BootImage.h" #include "KeymasterClient.h" #include "libavb/libavb.h" #include #include #include #include #include STATIC CONST CHAR8 *VerityMode = " androidboot.veritymode="; STATIC CONST CHAR8 *VerifiedState = " androidboot.verifiedbootstate="; STATIC CONST CHAR8 *KeymasterLoadState = " androidboot.keymaster=1"; STATIC CONST CHAR8 *DmVerityCmd = " root=/dev/dm-0 dm=\"system none ro,0 1 " "android-verity"; STATIC CONST CHAR8 *Space = " "; #define MAX_NUM_REQ_PARTITION 8 #define MAX_PROPERTY_SIZE 10 static CHAR8 *avb_verify_partition_name[] = { "boot", "dtbo", "vbmeta", "recovery", "vm-linux", "vendor-boot" }; STATIC struct verified_boot_verity_mode VbVm[] = { {FALSE, "logging"}, {TRUE, "enforcing"}, }; STATIC struct verified_boot_state_name VbSn[] = { {GREEN, "green"}, {ORANGE, "orange"}, {YELLOW, "yellow"}, {RED, "red"}, }; struct boolean_string { BOOLEAN value; CHAR8 *name; }; STATIC struct boolean_string BooleanString[] = { {FALSE, "false"}, {TRUE, "true"} }; typedef struct { AvbOps *Ops; AvbSlotVerifyData *SlotData; } VB2Data; UINT32 GetAVBVersion () { #if VERIFIED_BOOT_LE return AVB_LE; #elif VERIFIED_BOOT_2 return AVB_2; #elif VERIFIED_BOOT return AVB_1; #else return NO_AVB; #endif } BOOLEAN VerifiedBootEnbled () { return (GetAVBVersion () > NO_AVB); } STATIC EFI_STATUS AppendVBCmdLine (BootInfo *Info, CONST CHAR8 *Src) { EFI_STATUS Status = EFI_SUCCESS; INT32 SrcLen = AsciiStrLen (Src); CHAR8 *Dst = Info->VBCmdLine + Info->VBCmdLineFilledLen; INT32 DstLen = Info->VBCmdLineLen - Info->VBCmdLineFilledLen; GUARD (AsciiStrnCatS (Dst, DstLen, Src, SrcLen)); Info->VBCmdLineFilledLen += SrcLen; return EFI_SUCCESS; } STATIC EFI_STATUS AppendVBCommonCmdLine (BootInfo *Info) { EFI_STATUS Status = EFI_SUCCESS; if (Info->VbIntf->Revision >= QCOM_VERIFIEDBOOT_PROTOCOL_REVISION) { GUARD (AppendVBCmdLine (Info, VerifiedState)); GUARD (AppendVBCmdLine (Info, VbSn[Info->BootState].name)); } GUARD (AppendVBCmdLine (Info, KeymasterLoadState)); GUARD (AppendVBCmdLine (Info, Space)); return EFI_SUCCESS; } STATIC EFI_STATUS NoAVBLoadReqImage (BootInfo *Info, VOID **DtboImage, UINT32 *DtboSize, CHAR16 *Pname, CHAR16 *RequestedPartition) { EFI_STATUS Status = EFI_SUCCESS; Slot CurrentSlot; CHAR8 *AsciiPname = NULL; UINT64 PartSize = 0; AvbIOResult AvbStatus; AvbOpsUserData *UserData = NULL; AvbOps *Ops = NULL; GUARD ( StrnCpyS (Pname, (UINTN)MAX_GPT_NAME_SIZE, (CONST CHAR16 *)RequestedPartition, StrLen (RequestedPartition))); if (Info->MultiSlotBoot) { CurrentSlot = GetCurrentSlotSuffix (); GUARD ( StrnCatS (Pname, MAX_GPT_NAME_SIZE, CurrentSlot.Suffix, StrLen (CurrentSlot.Suffix))); } if (GetPartitionIndex (Pname) == INVALID_PTN) { Status = EFI_NO_MEDIA; goto out; } /* Get Partition size & compare with MAX supported size * If Partition size > DTBO_MAX_SIZE_ALLOWED return * Allocate Partition size memory otherwise */ UserData = avb_calloc (sizeof (AvbOpsUserData)); if (UserData == NULL) { DEBUG ((EFI_D_ERROR, "ERROR: Failed to allocate AvbOpsUserData\n")); Status = EFI_OUT_OF_RESOURCES; goto out; } Ops = AvbOpsNew (UserData); if (Ops == NULL) { DEBUG ((EFI_D_ERROR, "ERROR: Failed to allocate AvbOps\n")); Status = EFI_OUT_OF_RESOURCES; goto out; } AsciiPname = avb_calloc (MAX_GPT_NAME_SIZE); if (AsciiPname == NULL) { DEBUG ((EFI_D_ERROR, "ERROR: Failed to allocate AsciiPname\n")); Status = EFI_OUT_OF_RESOURCES; goto out; } UnicodeStrToAsciiStr (Pname, AsciiPname); AvbStatus = Ops->get_size_of_partition (Ops, AsciiPname, &PartSize); if (AvbStatus != AVB_IO_RESULT_OK || PartSize == 0) { DEBUG ((EFI_D_ERROR, "VB: Failed to get partition size " "(or) DTBO size is too big: 0x%x\n", PartSize)); Status = EFI_OUT_OF_RESOURCES; goto out; } if ((AsciiStrStr (AsciiPname, "dtbo")) && (PartSize > DTBO_MAX_SIZE_ALLOWED)) { DEBUG ((EFI_D_ERROR, "DTBO size is too big: 0x%x\n", PartSize)); Status = EFI_OUT_OF_RESOURCES; goto out; } DEBUG ((EFI_D_VERBOSE, "VB: Trying to allocate memory " "for DTBO: 0x%x\n", PartSize)); *DtboSize = (UINT32) PartSize; *DtboImage = AllocateZeroPool (PartSize); if (*DtboImage == NULL) { DEBUG ((EFI_D_ERROR, "VB: Unable to allocate memory for DTBO\n")); Status = EFI_OUT_OF_RESOURCES; goto out; } Status = LoadImageFromPartition (*DtboImage, DtboSize, Pname); out: if (Ops != NULL) { AvbOpsFree (Ops); } if (UserData != NULL) { avb_free (UserData); } if (AsciiPname != NULL) { avb_free (AsciiPname); } return Status; } STATIC EFI_STATUS VBCommonInit (BootInfo *Info) { EFI_STATUS Status = EFI_SUCCESS; Info->BootState = RED; Status = gBS->LocateProtocol (&gEfiQcomVerifiedBootProtocolGuid, NULL, (VOID **)&(Info->VbIntf)); if (Status != EFI_SUCCESS) { DEBUG ((EFI_D_ERROR, "Unable to locate VB protocol: %r\n", Status)); return Status; } return Status; } /* * Ensure Info->Pname is already updated before this function is called. * If Command line already has "root=", return TRUE, else FALSE. */ STATIC EFI_STATUS VBAllocateCmdLine (BootInfo *Info) { EFI_STATUS Status = EFI_SUCCESS; /* allocate VB command line*/ Info->VBCmdLine = AllocateZeroPool (DTB_PAD_SIZE); if (Info->VBCmdLine == NULL) { DEBUG ((EFI_D_ERROR, "VB CmdLine allocation failed!\n")); Status = EFI_OUT_OF_RESOURCES; return Status; } Info->VBCmdLineLen = DTB_PAD_SIZE; Info->VBCmdLineFilledLen = 0; Info->VBCmdLine[Info->VBCmdLineFilledLen] = '\0'; return Status; } STATIC BOOLEAN IsRootCmdLineUpdated (BootInfo *Info) { CHAR8* ImageCmdLine = NULL; ImageCmdLine = (CHAR8*) & (((boot_img_hdr*) (Info->Images[0].ImageBuffer))->cmdline[0]); ImageCmdLine[BOOT_ARGS_SIZE - 1] = '\0'; if (AsciiStrStr (ImageCmdLine, "root=")) { return TRUE; } else { return FALSE; } } /** Load Vendor Boot image if the boot image is v3 **/ STATIC EFI_STATUS NoAVBLoadVendorBootImage (BootInfo *Info) { EFI_STATUS Status; CHAR16 Pname[MAX_GPT_NAME_SIZE]; UINT8 ImgIdx = Info->NumLoadedImages; Status = NoAVBLoadReqImage (Info, (VOID **)&(Info->Images[ImgIdx].ImageBuffer), (UINT32 *)&(Info->Images[ImgIdx].ImageSize), Pname, (CHAR16 *)L"vendor-boot"); if (Status == EFI_NO_MEDIA) { DEBUG ((EFI_D_INFO, "No vendor-boot partition is found, Skipping\n")); if (Info->Images[ImgIdx].ImageBuffer != NULL) { FreePool (Info->Images[ImgIdx].ImageBuffer); } return EFI_SUCCESS; } else if (Status != EFI_SUCCESS) { DEBUG ((EFI_D_ERROR, "ERROR: Failed to load vendor-boot from partition: %r\n", Status)); if (Info->Images[ImgIdx].ImageBuffer != NULL) { goto Err; } } Info-> Images[ImgIdx].Name = AllocateZeroPool (StrLen (Pname) + 1); if (!Info-> Images[ImgIdx].Name) { Status = EFI_OUT_OF_RESOURCES; goto Err; } UnicodeStrToAsciiStr (Pname, Info->Images[ImgIdx].Name); Info-> NumLoadedImages++; return EFI_SUCCESS; Err: FreePool (Info->Images[ImgIdx].ImageBuffer); return Status; } STATIC EFI_STATUS LoadVendorBootImageHeader (BootInfo *Info, VOID **VendorImageHdrBuffer, UINT32 *VendorImageHdrSize) { EFI_STATUS Status = EFI_SUCCESS; CHAR16 Pname[MAX_GPT_NAME_SIZE] = {0}; StrnCpyS (Pname, ARRAY_SIZE (Pname), (CHAR16 *)L"vendor-boot", StrLen ((CHAR16 *)L"vendor-boot")); if (Info->MultiSlotBoot) { GUARD (StrnCatS (Pname, ARRAY_SIZE (Pname), GetCurrentSlotSuffix ().Suffix, StrLen (GetCurrentSlotSuffix ().Suffix))); } return LoadImageHeader (Pname, VendorImageHdrBuffer, VendorImageHdrSize); } STATIC EFI_STATUS LoadBootImageNoAuth (BootInfo *Info, UINT32 *PageSize, BOOLEAN *FastbootPath) { EFI_STATUS Status = EFI_SUCCESS; VOID *ImageHdrBuffer = NULL; UINT32 ImageHdrSize = 0; UINT32 ImageSizeActual = 0; VOID *VendorImageHdrBuffer = NULL; UINT32 VendorImageHdrSize = 0; BOOLEAN BootIntoRecovery = FALSE; BOOLEAN BootImageLoaded; /** The Images[0].ImageBuffer would have been loaded with the boot image * already if we are coming from fastboot boot path. Ignore loading it * again. **/ BootImageLoaded = (Info->Images[0].ImageBuffer != NULL) && (Info->Images[0].ImageSize > 0); *FastbootPath = BootImageLoaded; if (BootImageLoaded) { ImageHdrBuffer = Info->Images[0].ImageBuffer; ImageHdrSize = BOOT_IMG_MAX_PAGE_SIZE; } else { Status = LoadImageHeader (Info->Pname, &ImageHdrBuffer, &ImageHdrSize); if (Status != EFI_SUCCESS || ImageHdrBuffer == NULL) { DEBUG ((EFI_D_ERROR, "ERROR: Failed to load image header: %r\n", Status)); return Status; } else if (ImageHdrSize < sizeof (boot_img_hdr)) { DEBUG ((EFI_D_ERROR, "ERROR: Invalid image header size: %u\n", ImageHdrSize)); return EFI_BAD_BUFFER_SIZE; } BootIntoRecovery = Info->BootIntoRecovery; } Info->HeaderVersion = ((boot_img_hdr *)(ImageHdrBuffer))->header_version; /* Additional vendor-boot image header needs be loaded for header * versions than 3. Consider both the headers for validation. */ if (Info->HeaderVersion >= BOOT_HEADER_VERSION_THREE) { Status = LoadVendorBootImageHeader (Info, &VendorImageHdrBuffer, &VendorImageHdrSize); if (Status != EFI_SUCCESS) { DEBUG ((EFI_D_ERROR, "ERROR: Failed to load vendor-boot Image header: %r\n", Status)); goto ErrV3; } } /* Add check for boot image header, kernel page size, * and ensure kernel command line is terminate. */ Status = CheckImageHeader (ImageHdrBuffer, ImageHdrSize, VendorImageHdrBuffer, VendorImageHdrSize, &ImageSizeActual, PageSize, BootIntoRecovery); if (Status != EFI_SUCCESS) { DEBUG ((EFI_D_ERROR, "Invalid boot image header:%r\n", Status)); goto Err; } if (!BootImageLoaded) { Status = LoadImage (Info->Pname, (VOID **)&(Info->Images[0].ImageBuffer), ImageSizeActual, *PageSize); if (Status != EFI_SUCCESS) { DEBUG ((EFI_D_ERROR, "ERROR: Failed to load image from partition: %r\n", Status)); goto Err; } Info->Images[0].Name = AllocateZeroPool (StrLen (Info->Pname) + 1); if (!Info->Images[0].Name) { Status = EFI_OUT_OF_RESOURCES; goto ErrImg; } UnicodeStrToAsciiStr (Info->Pname, Info->Images[0].Name); Info->NumLoadedImages = 1; } Info->Images[0].ImageSize = ImageSizeActual; if (Info->HeaderVersion >= BOOT_HEADER_VERSION_THREE) { Status = NoAVBLoadVendorBootImage (Info); if (Status != EFI_SUCCESS) { DEBUG ((EFI_D_ERROR, "ERROR: Failed to load vendor-boot Image : %r\n", Status)); goto ErrImgName; } } return EFI_SUCCESS; ErrImgName: if (!BootImageLoaded && Info->Images[0].Name) { FreePool (Info->Images[0].Name); } ErrImg: if (!BootImageLoaded && Info->Images[0].ImageBuffer) { UINT32 ImageSize = ADD_OF (ROUND_TO_PAGE (ImageSizeActual, (*PageSize - 1)), *PageSize); FreePages (Info->Images[0].ImageBuffer, ALIGN_PAGES (ImageSize, ALIGNMENT_MASK_4KB)); } Err: if (VendorImageHdrBuffer) { FreePages (VendorImageHdrBuffer, ALIGN_PAGES (BOOT_IMG_MAX_PAGE_SIZE, ALIGNMENT_MASK_4KB)); } ErrV3: if (!BootImageLoaded && ImageHdrBuffer) { FreePages (ImageHdrBuffer, ALIGN_PAGES (BOOT_IMG_MAX_PAGE_SIZE, ALIGNMENT_MASK_4KB)); } return Status; } STATIC EFI_STATUS LoadImageNoAuth (BootInfo *Info) { EFI_STATUS Status = EFI_SUCCESS; CHAR16 Pname[MAX_GPT_NAME_SIZE]; UINTN *ImgIdx = &Info->NumLoadedImages; UINT32 PageSize = 0; BOOLEAN FastbootPath; Status = LoadBootImageNoAuth (Info, &PageSize, &FastbootPath); if (Status != EFI_SUCCESS) { return Status; } /*load dt overlay when avb is disabled*/ Status = NoAVBLoadReqImage (Info, (VOID **)&(Info->Images[*ImgIdx].ImageBuffer), (UINT32 *)&(Info->Images[*ImgIdx].ImageSize), Pname, (CHAR16 *)L"dtbo"); if (Status == EFI_NO_MEDIA) { DEBUG ((EFI_D_INFO, "No dtbo partition is found, Skip dtbo\n")); if (Info->Images[*ImgIdx].ImageBuffer != NULL) { FreePool (Info->Images[*ImgIdx].ImageBuffer); } } else if (Status != EFI_SUCCESS) { DEBUG ((EFI_D_ERROR, "ERROR: Failed to load dtbo from partition: %r\n", Status)); Status = EFI_LOAD_ERROR; goto Err; } else { /* EFI_SUCCESS */ Info-> Images[*ImgIdx].Name = AllocateZeroPool (StrLen (Pname) + 1); if (!Info->Images[*ImgIdx].Name) { Status = EFI_OUT_OF_RESOURCES; goto Err; } UnicodeStrToAsciiStr (Pname, Info->Images[*ImgIdx].Name); ++(*ImgIdx); } /* Load vm-linux if Verified boot is disabled */ if (IsVmEnabled ()) { Status = NoAVBLoadReqImage (Info, (VOID **)&(Info->Images[*ImgIdx].ImageBuffer), (UINT32 *)&(Info->Images[*ImgIdx].ImageSize), Pname, L"vm-linux"); if (Status == EFI_NO_MEDIA) { DEBUG ((EFI_D_INFO, "No vm-linux partition is found, Skip..\n")); goto Out; } else if (Status != EFI_SUCCESS) { DEBUG ((EFI_D_ERROR, "ERROR: Failed to load vm-linux from partition: %r\n", Status)); goto Err; } Info-> Images[*ImgIdx].Name = AllocateZeroPool (StrLen (Pname) + 1); if (!Info->Images[*ImgIdx].Name) { Status = EFI_OUT_OF_RESOURCES; goto Err; } UnicodeStrToAsciiStr (Pname, Info->Images[*ImgIdx].Name); ++(*ImgIdx); } Out: return EFI_SUCCESS; Err: /* Free all the Images' memory that was allocated */ for (--(*ImgIdx); *ImgIdx; --(*ImgIdx)) { if (Info->Images[*ImgIdx].ImageBuffer != NULL) { FreePool (Info->Images[*ImgIdx].ImageBuffer); } if (Info->Images[*ImgIdx].Name != NULL) { FreePool (Info->Images[*ImgIdx].Name); } } /* Images[0] needs to be freed in a special way as it was allocated * using AllocPages(). Although, ignore if we are coming from a * fastboot boot path. */ if (FastbootPath) goto err_out; if (Info->Images[0].ImageBuffer) { UINT32 ImageSize = ADD_OF (ROUND_TO_PAGE (Info->Images[0].ImageSize, (PageSize - 1)), PageSize); FreePages (Info->Images[0].ImageBuffer, ALIGN_PAGES (ImageSize, ALIGNMENT_MASK_4KB)); } if (Info->Images[0].Name) { FreePool (Info->Images[0].Name); } err_out: return Status; } STATIC EFI_STATUS LoadImageNoAuthWrapper (BootInfo *Info) { EFI_STATUS Status = EFI_SUCCESS; CHAR8 *SystemPath = NULL; UINT32 SystemPathLen = 0; GUARD (VBAllocateCmdLine (Info)); GUARD (LoadImageNoAuth (Info)); if (!IsDynamicPartitionSupport () && !IsRootCmdLineUpdated (Info)) { SystemPathLen = GetSystemPath (&SystemPath, Info->MultiSlotBoot, Info->BootIntoRecovery, (CHAR16 *)L"system", (CHAR8 *)"root"); // XXX: Linux bootloader system partition doesn't exists // try to lookup for rootfs if that the case. if (SystemPathLen == 0 || SystemPath == NULL) { SystemPathLen = GetSystemPath (&SystemPath, FALSE, Info->BootIntoRecovery, (CHAR16 *)L"rootfs", (CHAR8 *)"root"); } if (SystemPathLen == 0 || SystemPath == NULL) { DEBUG ((EFI_D_ERROR, "GetSystemPath failed!\n")); return EFI_LOAD_ERROR; } GUARD (AppendVBCmdLine (Info, SystemPath)); } return Status; } STATIC EFI_STATUS LoadImageAndAuthVB1 (BootInfo *Info) { EFI_STATUS Status = EFI_SUCCESS; CHAR8 StrPnameAscii[MAX_GPT_NAME_SIZE]; /* partition name starting with / and no suffix */ CHAR8 PnameAscii[MAX_GPT_NAME_SIZE]; CHAR8 *SystemPath = NULL; UINT32 SystemPathLen = 0; CHAR8 *Temp = NULL; GUARD (VBCommonInit (Info)); GUARD (VBAllocateCmdLine (Info)); GUARD (LoadImageNoAuth (Info)); device_info_vb_t DevInfo_vb; DevInfo_vb.is_unlocked = IsUnlocked (); DevInfo_vb.is_unlock_critical = IsUnlockCritical (); Status = Info->VbIntf->VBDeviceInit (Info->VbIntf, (device_info_vb_t *)&DevInfo_vb); if (Status != EFI_SUCCESS) { DEBUG ((EFI_D_ERROR, "Error during VBDeviceInit: %r\n", Status)); return Status; } AsciiStrnCpyS (StrPnameAscii, ARRAY_SIZE (StrPnameAscii), "/", AsciiStrLen ("/")); UnicodeStrToAsciiStr (Info->Pname, PnameAscii); if (Info->MultiSlotBoot) { AsciiStrnCatS (StrPnameAscii, ARRAY_SIZE (StrPnameAscii), PnameAscii, AsciiStrLen (PnameAscii) - (MAX_SLOT_SUFFIX_SZ - 1)); } else { AsciiStrnCatS (StrPnameAscii, ARRAY_SIZE (StrPnameAscii), PnameAscii, AsciiStrLen (PnameAscii)); } Status = Info->VbIntf->VBVerifyImage (Info->VbIntf, (UINT8 *)StrPnameAscii, (UINT8 *)Info->Images[0].ImageBuffer, Info->Images[0].ImageSize, &Info->BootState); if (Status != EFI_SUCCESS || Info->BootState == BOOT_STATE_MAX) { DEBUG ((EFI_D_ERROR, "VBVerifyImage failed with: %r\n", Status)); return Status; } Status = Info->VbIntf->VBSendRot (Info->VbIntf); if (Status != EFI_SUCCESS) { DEBUG ((EFI_D_ERROR, "Error sending Rot : %r\n", Status)); return Status; } if (!IsRootCmdLineUpdated (Info)) { SystemPathLen = GetSystemPath (&SystemPath, Info->MultiSlotBoot, Info->BootIntoRecovery, (CHAR16 *)L"system", (CHAR8 *)"root"); if (SystemPathLen == 0 || SystemPath == NULL) { DEBUG ((EFI_D_ERROR, "GetSystemPath failed!\n")); return EFI_LOAD_ERROR; } GUARD (AppendVBCmdLine (Info, DmVerityCmd)); /* Copy only the portion after "root=" in the SystemPath */ Temp = AsciiStrStr (SystemPath, "root="); if (Temp) { CopyMem (Temp, SystemPath + AsciiStrLen ("root=") + 1, SystemPathLen - AsciiStrLen ("root=") - 1); SystemPath[SystemPathLen - AsciiStrLen ("root=")] = '\0'; } GUARD (AppendVBCmdLine (Info, SystemPath)); GUARD (AppendVBCmdLine (Info, "\"")); } GUARD (AppendVBCommonCmdLine (Info)); GUARD (AppendVBCmdLine (Info, VerityMode)); GUARD (AppendVBCmdLine (Info, VbVm[IsEnforcing ()].name)); Info->VBData = NULL; return Status; } STATIC BOOLEAN ResultShouldContinue (AvbSlotVerifyResult Result) { switch (Result) { case AVB_SLOT_VERIFY_RESULT_ERROR_OOM: case AVB_SLOT_VERIFY_RESULT_ERROR_IO: case AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA: case AVB_SLOT_VERIFY_RESULT_ERROR_UNSUPPORTED_VERSION: case AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_ARGUMENT: return FALSE; case AVB_SLOT_VERIFY_RESULT_OK: case AVB_SLOT_VERIFY_RESULT_ERROR_VERIFICATION: case AVB_SLOT_VERIFY_RESULT_ERROR_ROLLBACK_INDEX: case AVB_SLOT_VERIFY_RESULT_ERROR_PUBLIC_KEY_REJECTED: return TRUE; } return FALSE; } STATIC EFI_STATUS LEGetImageHash (QcomAsn1x509Protocol *pEfiQcomASN1X509Protocol, VB_HASH HashAlgorithm, UINT8 *Img, UINTN ImgSize, UINT8 *ImgHash, UINTN HashSize) { EFI_STATUS Status = EFI_FAILURE; EFI_GUID *HashAlgorithmGuid; UINTN DigestSize = 0; EFI_HASH2_OUTPUT Hash2Output; EFI_HASH2_PROTOCOL *pEfiHash2Protocol = NULL; if (pEfiQcomASN1X509Protocol == NULL || Img == NULL || ImgHash == NULL) { DEBUG ((EFI_D_ERROR, "LEGetRSAPublicKeyInfoFromCertificate: Invalid pointer\n")); return EFI_INVALID_PARAMETER; } switch (HashAlgorithm) { case VB_SHA256: HashAlgorithmGuid = &gEfiHashAlgorithmSha256Guid; break; default: DEBUG ((EFI_D_ERROR, "VB: LEGetImageHash: not supported algorithm:%d\n", HashAlgorithm)); Status = EFI_UNSUPPORTED; goto exit; } Status = gBS->LocateProtocol (&gEfiHash2ProtocolGuid, NULL, (VOID **)&pEfiHash2Protocol); if (Status != EFI_SUCCESS) { DEBUG ((EFI_D_ERROR, "VB:LEGetImageHash: LocateProtocol unsuccessful!Status: %r\n", Status)); goto exit; } Status = pEfiHash2Protocol->GetHashSize (pEfiHash2Protocol, HashAlgorithmGuid, &DigestSize); if (Status != EFI_SUCCESS) { DEBUG ((EFI_D_ERROR, "VB: LEGetImageHash: GetHashSize unsuccessful! Status: %r\n", Status)); goto exit; } if (HashSize != DigestSize) { DEBUG ((EFI_D_ERROR, "VB: LEGetImageHash: Invalid size! HashSize: %d, DigestSize: %d\n" , HashSize, DigestSize)); Status = EFI_FAILURE; goto exit; } Status = pEfiHash2Protocol->HashInit (pEfiHash2Protocol, HashAlgorithmGuid); if (Status != EFI_SUCCESS) { DEBUG ((EFI_D_ERROR, "VB: LEGetImageHash: HashInit unsuccessful! Status: %r\n", Status)); goto exit; } Status = pEfiHash2Protocol->HashUpdate (pEfiHash2Protocol, Img, ImgSize); if (EFI_SUCCESS != Status) { DEBUG ((EFI_D_ERROR, "VB: LEGetImageHash: HashUpdate unsuccessful(Img)!Status: %r\n" , Status)); goto exit; } Status = pEfiHash2Protocol->HashFinal (pEfiHash2Protocol, &Hash2Output); if (EFI_SUCCESS != Status) { DEBUG ((EFI_D_ERROR, "VB: LEGetImageHash: HashFinal unsuccessful! Status: %r\n", Status)); goto exit; } gBS->CopyMem ((VOID *)ImgHash, (VOID *)&Hash2Output, DigestSize); Status = EFI_SUCCESS; exit: return Status; } STATIC EFI_STATUS LEGetRSAPublicKeyInfoFromCertificate ( QcomAsn1x509Protocol *pEfiQcomASN1X509Protocol, CERTIFICATE *Certificate, secasn1_data_type *Modulus, secasn1_data_type *PublicExp, UINT32 *PaddingType) { EFI_STATUS Status = EFI_FAILURE; RSA RsaKey = {NULL}; if (pEfiQcomASN1X509Protocol == NULL || Certificate == NULL || Modulus == NULL || PublicExp == NULL || PaddingType == NULL) { DEBUG ((EFI_D_ERROR, "LEGetRSAPublicKeyInfoFromCertificate: Invalid pointer\n")); return EFI_INVALID_PARAMETER; } Status = pEfiQcomASN1X509Protocol->ASN1X509GetRSAFromCert (pEfiQcomASN1X509Protocol, Certificate, &RsaKey); if (Status != EFI_SUCCESS) { DEBUG ((EFI_D_ERROR, "VB: ASN1X509GetRSAFromCert unsuccessful! Status : %r\n", Status)); goto exit; } Status = pEfiQcomASN1X509Protocol->ASN1X509GetKeymaterial (pEfiQcomASN1X509Protocol, &RsaKey, Modulus, PublicExp); if (Status != EFI_SUCCESS) { DEBUG ((EFI_D_ERROR, "VB: ASN1X509GetKeymaterial unsuccessful! Status: %r\n", Status)); goto exit; } *PaddingType = CE_RSA_PAD_PKCS1_V1_5_SIG; exit: return Status; } STATIC EFI_STATUS LEVerifyHashWithRSASignature ( UINT8 *ImgHash, VB_HASH HashAlgorithm, secasn1_data_type *Modulus, secasn1_data_type *PublicExp, UINT32 PaddingType, CONST UINT8 *SignaturePtr, UINT32 SignatureLen) { EFI_STATUS Status = EFI_FAILURE; CE_RSA_KEY Key = {0}; BigInt ModulusBi; BigInt PublicExpBi; INT32 HashIdx; INT32 HashLen; VOID *PaddingInfo = NULL; QcomSecRsaProtocol *pEfiQcomSecRSAProtocol = NULL; SetMem (&Key, sizeof (CE_RSA_KEY), 0); if (ImgHash == NULL || Modulus == NULL || PublicExp == NULL || SignaturePtr == NULL) { DEBUG ((EFI_D_ERROR, "LEVerifyHashWithRSASignature:Invalid pointer\n")); return EFI_INVALID_PARAMETER; } switch (HashAlgorithm) { case VB_SHA256: HashIdx = CE_HASH_IDX_SHA256; HashLen = VB_SHA256_SIZE; break; default: DEBUG ((EFI_D_ERROR, "VB: LEVerifySignature: Hash algorithm not supported\n")); Status = EFI_UNSUPPORTED; goto exit; } Key.N = AllocateZeroPool (sizeof (S_BIGINT)); if (Key.N == NULL) { DEBUG ((EFI_D_ERROR, "VB: LEVerifySignature: mem allocation err for Key.N\n")); goto exit; } Key.e = AllocateZeroPool (sizeof (S_BIGINT)); if (Key.e == NULL) { DEBUG ((EFI_D_ERROR, "VB: LEVerifySignature: mem allocation err for Key.e\n")); goto exit; } Status = gBS->LocateProtocol (&gEfiQcomSecRSAProtocolGuid, NULL, (VOID **) &pEfiQcomSecRSAProtocol); if ( Status != EFI_SUCCESS) { DEBUG ((EFI_D_ERROR, "VB: LEVerifySignature: LocateProtocol failed, Status: %r\n", Status)); goto exit; } Status = pEfiQcomSecRSAProtocol->SecRSABigIntReadBin ( pEfiQcomSecRSAProtocol, Modulus->data, Modulus->Len, &ModulusBi); if ( Status != EFI_SUCCESS) { DEBUG ((EFI_D_ERROR, "VB: LEVerifySignature: SecRSABigIntReadBin for Modulus failed!" "Status: %r\n", Status)); goto exit; } Status = pEfiQcomSecRSAProtocol->SecRSABigIntReadBin ( pEfiQcomSecRSAProtocol, PublicExp->data, PublicExp->Len, &PublicExpBi); if ( Status != EFI_SUCCESS) { DEBUG ((EFI_D_ERROR, "VB: LEVerifySignature: SecRSABigIntReadBin for" " Modulus failed! Status: %r\n", Status)); goto exit; } Key.N->Bi = ModulusBi; Key.e->Bi = PublicExpBi; Key.e->Sign = S_BIGINT_POS; Key.Type = CE_RSA_KEY_PUBLIC; Status = pEfiQcomSecRSAProtocol->SecRSAVerifySig (pEfiQcomSecRSAProtocol, &Key, PaddingType, PaddingInfo, HashIdx, ImgHash, HashLen, (UINT8*)SignaturePtr, SignatureLen); if (Status != EFI_SUCCESS) { DEBUG ((EFI_D_ERROR, "VB: LEVerifySignature: SecRSAVerifySig failed! Status: %r\n", Status)); goto exit; } DEBUG ((EFI_D_VERBOSE, "VB: LEVerifySignature: SecRSAVerifySig success!" " Status: %r\n", Status)); Status = EFI_SUCCESS; exit: if (Key.N != NULL) { FreePool (Key.N); } if (Key.e != NULL) { FreePool (Key.e); } return Status; } STATIC EFI_STATUS LEVerifyHashWithSignature ( QcomAsn1x509Protocol *pEfiQcomASN1X509Protocol, UINT8 *ImgHash, VB_HASH HashAlgorithm, CERTIFICATE *Certificate, CONST UINT8 *SignaturePtr, UINT32 SignatureLen) { EFI_STATUS Status = EFI_FAILURE; secasn1_data_type Modulus = {NULL}; secasn1_data_type PublicExp = {NULL}; UINT32 PaddingType = 0; if (pEfiQcomASN1X509Protocol == NULL || ImgHash == NULL || Certificate == NULL || SignaturePtr == NULL) { DEBUG ((EFI_D_ERROR, "LEVerifyHashWithSignature: Invalid pointer\n")); return EFI_INVALID_PARAMETER; } /* TODO: get subject publick key info from certificate, implement new algorithm in XBL*/ /* XBL implemented by default sha256 and rsaEncryption with PKCS1_V1_5 padding*/ Status = LEGetRSAPublicKeyInfoFromCertificate (pEfiQcomASN1X509Protocol, Certificate, &Modulus, &PublicExp, &PaddingType); if (Status != EFI_SUCCESS) { DEBUG ((EFI_D_ERROR, "VB: LEGetRSAPublicKeyInfoFromCertificate " "unsuccessful! Status: %r\n", Status)); goto exit; } Status = LEVerifyHashWithRSASignature (ImgHash, HashAlgorithm, &Modulus, &PublicExp, PaddingType, SignaturePtr, SignatureLen); if (Status != EFI_SUCCESS) { DEBUG ((EFI_D_ERROR, "VB: LEVerifyHashWithSignature unsuccessful! " "Status: %r\n", Status)); goto exit; } Status = EFI_SUCCESS; exit: return Status; } static BOOLEAN GetHeaderVersion (AvbSlotVerifyData *SlotData) { BOOLEAN HeaderVersion = 0; UINTN LoadedIndex = 0; for (LoadedIndex = 0; LoadedIndex < SlotData->num_loaded_partitions; LoadedIndex++) { if (avb_strcmp (SlotData->loaded_partitions[LoadedIndex].partition_name, "recovery") == 0 ) return ( (boot_img_hdr *) (SlotData->loaded_partitions[LoadedIndex].data))->header_version; } return HeaderVersion; } static VOID AddRequestedPartition (CHAR8 **RequestedPartititon, UINT32 Index) { UINTN PartIndex = 0; for (PartIndex = 0; PartIndex < MAX_NUM_REQ_PARTITION; PartIndex++) { if (RequestedPartititon[PartIndex] == NULL) { RequestedPartititon[PartIndex] = avb_verify_partition_name[Index]; break; } } } STATIC VOID ComputeVbMetaDigest (AvbSlotVerifyData* SlotData, CHAR8* Digest) { size_t Index; AvbSHA256Ctx Ctx; avb_sha256_init (&Ctx); for (Index = 0; Index < SlotData->num_vbmeta_images; Index++) { avb_sha256_update (&Ctx, SlotData->vbmeta_images[Index].vbmeta_data, SlotData->vbmeta_images[Index].vbmeta_size); } avb_memcpy (Digest, avb_sha256_final(&Ctx), AVB_SHA256_DIGEST_SIZE); } static UINT32 ParseBootSecurityLevel (CONST CHAR8 *BootSecurityLevel, size_t BootSecurityLevelSize) { UINT32 PatchLevelDate = 0; UINT32 PatchLevelMonth = 0; UINT32 PatchLevelYear = 0; UINT32 SeparatorCount = 0; UINT32 Count = 0; /*Parse the value of security patch as per YYYY-MM-DD format*/ while (Count < BootSecurityLevelSize) { if (BootSecurityLevel[Count] == '-') { SeparatorCount++; } else if (SeparatorCount == 2) { PatchLevelDate *= 10; PatchLevelDate += (BootSecurityLevel[Count] - '0'); } else if (SeparatorCount == 1) { PatchLevelMonth *= 10; PatchLevelMonth += (BootSecurityLevel[Count] - '0'); } else if (SeparatorCount == 0) { PatchLevelYear *= 10; PatchLevelYear += (BootSecurityLevel[Count] - '0'); } else { return -1; } Count++; } PatchLevelDate = PatchLevelDate << 11; PatchLevelYear = (PatchLevelYear - 2000) << 4; return (PatchLevelDate | PatchLevelYear | PatchLevelMonth); } STATIC BOOLEAN IsValidPartition (Slot *Slot, CONST CHAR16 *Name) { CHAR16 PartiName[MAX_GPT_NAME_SIZE] = {0}; EFI_STATUS Status; INT32 Index; GUARD (StrnCpyS (PartiName, (UINTN)MAX_GPT_NAME_SIZE, Name, StrLen (Name))); /* If *Slot is filled, it means that it's for multi-slot */ if (Slot) { GUARD (StrnCatS (PartiName, MAX_GPT_NAME_SIZE, Slot->Suffix, StrLen (Slot->Suffix))); } Index = GetPartitionIndex (PartiName); return (Index == INVALID_PTN || Index >= MAX_NUM_PARTITIONS) ? FALSE : TRUE; } STATIC EFI_STATUS LoadImageAndAuthVB2 (BootInfo *Info) { EFI_STATUS Status = EFI_SUCCESS; AvbSlotVerifyResult Result; AvbSlotVerifyData *SlotData = NULL; VB2Data *VBData = NULL; AvbOpsUserData *UserData = NULL; AvbOps *Ops = NULL; CHAR8 PnameAscii[MAX_GPT_NAME_SIZE] = {0}; CHAR8 *SlotSuffix = NULL; BOOLEAN AllowVerificationError = IsUnlocked (); CHAR8 *RequestedPartitionAll[MAX_NUM_REQ_PARTITION] = {NULL}; CHAR8 **RequestedPartition = NULL; UINTN NumRequestedPartition = 0; UINT32 ImageHdrSize = BOOT_IMG_MAX_PAGE_SIZE; UINT32 PageSize = 0; UINT32 ImageSizeActual = 0; VOID *ImageBuffer = NULL; UINTN ImageSize = 0; VOID *VendorBootImageBuffer = NULL; UINTN VendorBootImageSize = 0; KMRotAndBootState Data = {0}; CONST CHAR8 *BootSecurityLevel = NULL; size_t BootSecurityLevelSize = 0; BOOLEAN DateSupport = FALSE; CONST boot_img_hdr *BootImgHdr = NULL; AvbSlotVerifyFlags VerifyFlags = AllowVerificationError ? AVB_SLOT_VERIFY_FLAGS_ALLOW_VERIFICATION_ERROR : AVB_SLOT_VERIFY_FLAGS_NONE; AvbHashtreeErrorMode VerityFlags = AVB_HASHTREE_ERROR_MODE_RESTART_AND_INVALIDATE; CHAR8 Digest[AVB_SHA256_DIGEST_SIZE]; BOOLEAN UpdateRollback = FALSE; UINT32 OSVersion; Info->BootState = RED; GUARD (VBCommonInit (Info)); GUARD (VBAllocateCmdLine (Info)); UserData = avb_calloc (sizeof (AvbOpsUserData)); if (UserData == NULL) { DEBUG ((EFI_D_ERROR, "ERROR: Failed to allocate AvbOpsUserData\n")); Status = EFI_OUT_OF_RESOURCES; goto out; } Ops = AvbOpsNew (UserData); if (Ops == NULL) { DEBUG ((EFI_D_ERROR, "ERROR: Failed to allocate AvbOps\n")); Status = EFI_OUT_OF_RESOURCES; goto out; } UserData->IsMultiSlot = Info->MultiSlotBoot; if (Info->MultiSlotBoot) { UnicodeStrToAsciiStr (Info->Pname, PnameAscii); if ((MAX_SLOT_SUFFIX_SZ + 1) > AsciiStrLen (PnameAscii)) { DEBUG ((EFI_D_ERROR, "ERROR: Can not determine slot suffix\n")); Status = EFI_INVALID_PARAMETER; goto out; } SlotSuffix = &PnameAscii[AsciiStrLen (PnameAscii) - MAX_SLOT_SUFFIX_SZ + 1]; } else { SlotSuffix = "\0"; } /* Check if slot is bootable and call into TZ to update rollback version. * This is similar to the update done later for vbmeta bound images. * However, here we call into TZ irrespective of version check and let * the secure environment make the decision on updating rollback version */ UpdateRollback = avb_should_update_rollback (UserData->IsMultiSlot); if (UpdateRollback) { Status = UpdateRollbackSyscall (); if (Status != EFI_SUCCESS) { DEBUG ((EFI_D_ERROR, "LoadImageAndAuthVB2: Error int TZ Rollback Version " "syscall; ScmCall Status: (0x%x)\r\n", Status)); return Status; } } DEBUG ((EFI_D_VERBOSE, "Slot: %a, allow verification error: %a\n", SlotSuffix, BooleanString[AllowVerificationError].name)); if (FixedPcdGetBool (AllowEio)) { VerityFlags = IsEnforcing () ? AVB_HASHTREE_ERROR_MODE_RESTART : AVB_HASHTREE_ERROR_MODE_EIO; } else { VerityFlags = AVB_HASHTREE_ERROR_MODE_RESTART_AND_INVALIDATE; } RequestedPartition = RequestedPartitionAll; if ( ( (!Info->MultiSlotBoot) || IsDynamicPartitionSupport ()) && Info->BootIntoRecovery) { AddRequestedPartition (RequestedPartitionAll, IMG_RECOVERY); NumRequestedPartition += 1; Result = avb_slot_verify (Ops, (CONST CHAR8 *CONST *)RequestedPartition, SlotSuffix, VerifyFlags, VerityFlags, &SlotData); if (AllowVerificationError && ResultShouldContinue (Result)) { DEBUG ((EFI_D_VERBOSE, "State: Unlocked, AvbSlotVerify returned " "%a, continue boot\n", avb_slot_verify_result_to_string (Result))); } else if (Result != AVB_SLOT_VERIFY_RESULT_OK) { DEBUG ((EFI_D_ERROR, "ERROR: Device State %a,AvbSlotVerify returned %a\n", AllowVerificationError ? "Unlocked" : "Locked", avb_slot_verify_result_to_string (Result))); Status = EFI_LOAD_ERROR; Info->BootState = RED; goto out; } if (SlotData == NULL) { Status = EFI_LOAD_ERROR; Info->BootState = RED; goto out; } BOOLEAN HeaderVersion = GetHeaderVersion (SlotData); DEBUG ( (EFI_D_VERBOSE, "Recovery HeaderVersion %d \n", HeaderVersion)); if (HeaderVersion == BOOT_HEADER_VERSION_ZERO || HeaderVersion >= BOOT_HEADER_VERSION_THREE) { AddRequestedPartition (RequestedPartitionAll, IMG_DTBO); NumRequestedPartition += 1; if (SlotData != NULL) { avb_slot_verify_data_free (SlotData); } Result = avb_slot_verify (Ops, (CONST CHAR8 *CONST *)RequestedPartition, SlotSuffix, VerifyFlags, VerityFlags, &SlotData); } } else { Slot CurrentSlot; if (!Info->NumLoadedImages) { AddRequestedPartition (RequestedPartitionAll, IMG_BOOT); NumRequestedPartition += 1; } AddRequestedPartition (RequestedPartitionAll, IMG_DTBO); NumRequestedPartition += 1; if (Info->MultiSlotBoot) { CurrentSlot = GetCurrentSlotSuffix (); } if (IsVmEnabled ()) { if (IsValidPartition (&CurrentSlot, L"vm-linux")) { AddRequestedPartition (RequestedPartitionAll, IMG_VMLINUX); NumRequestedPartition += 1; } else { DEBUG ((EFI_D_VERBOSE, "Invalid vm-linux partition. Skipping\n")); } } if (IsValidPartition (&CurrentSlot, L"vendor-boot")) { AddRequestedPartition (RequestedPartitionAll, IMG_VENDOR_BOOT); NumRequestedPartition += 1; } else { DEBUG ((EFI_D_VERBOSE, "Invalid vendor-boot partition. Skipping\n")); } Result = avb_slot_verify (Ops, (CONST CHAR8 *CONST *)RequestedPartition, SlotSuffix, VerifyFlags, VerityFlags, &SlotData); } if (SlotData == NULL) { Status = EFI_LOAD_ERROR; Info->BootState = RED; goto out; } if (AllowVerificationError && ResultShouldContinue (Result)) { DEBUG ((EFI_D_VERBOSE, "State: Unlocked, AvbSlotVerify returned " "%a, continue boot\n", avb_slot_verify_result_to_string (Result))); } else if (Result != AVB_SLOT_VERIFY_RESULT_OK) { DEBUG ((EFI_D_ERROR, "ERROR: Device State %a, AvbSlotVerify returned %a\n", AllowVerificationError ? "Unlocked" : "Locked", avb_slot_verify_result_to_string (Result))); Status = EFI_LOAD_ERROR; Info->BootState = RED; goto out; } for (UINTN ReqIndex = 0; ReqIndex < NumRequestedPartition; ReqIndex++) { DEBUG ((EFI_D_VERBOSE, "Requested Partition: %a\n", RequestedPartition[ReqIndex])); for (UINTN LoadedIndex = 0; LoadedIndex < SlotData->num_loaded_partitions; LoadedIndex++) { DEBUG ((EFI_D_VERBOSE, "Loaded Partition: %a\n", SlotData->loaded_partitions[LoadedIndex].partition_name)); if (!AsciiStrnCmp ( RequestedPartition[ReqIndex], SlotData->loaded_partitions[LoadedIndex].partition_name, AsciiStrLen ( SlotData->loaded_partitions[LoadedIndex].partition_name))) { if (Info->NumLoadedImages >= ARRAY_SIZE (Info->Images)) { DEBUG ((EFI_D_ERROR, "NumLoadedPartition" "(%d) too large " "max images(%d)\n", Info->NumLoadedImages, ARRAY_SIZE (Info->Images))); Status = EFI_LOAD_ERROR; Info->BootState = RED; goto out; } Info->Images[Info->NumLoadedImages].Name = SlotData->loaded_partitions[LoadedIndex].partition_name; Info->Images[Info->NumLoadedImages].ImageBuffer = SlotData->loaded_partitions[LoadedIndex].data; Info->Images[Info->NumLoadedImages].ImageSize = SlotData->loaded_partitions[LoadedIndex].data_size; Info->NumLoadedImages++; break; } } } if (Info->NumLoadedImages < NumRequestedPartition) { DEBUG ((EFI_D_ERROR, "ERROR: AvbSlotVerify slot data error: num of " "loaded partitions %d, requested %d\n", Info->NumLoadedImages, NumRequestedPartition)); Status = EFI_LOAD_ERROR; goto out; } DEBUG ((EFI_D_VERBOSE, "Total loaded partition %d\n", Info->NumLoadedImages)); VBData = (VB2Data *)avb_calloc (sizeof (VB2Data)); if (VBData == NULL) { DEBUG ((EFI_D_ERROR, "ERROR: Failed to allocate VB2Data\n")); Status = EFI_OUT_OF_RESOURCES; goto out; } VBData->Ops = Ops; VBData->SlotData = SlotData; Info->VBData = (VOID *)VBData; GUARD_OUT (GetImage (Info, &ImageBuffer, &ImageSize, ( (!Info->MultiSlotBoot || IsDynamicPartitionSupport ()) && Info->BootIntoRecovery) ? "recovery" : "boot")); if (ImageSize < sizeof (boot_img_hdr)) { DEBUG ((EFI_D_ERROR, "Invalid boot image header size: %u\n", ImageSize)); Status = EFI_BAD_BUFFER_SIZE; goto out; } BootImgHdr = (boot_img_hdr *)ImageBuffer; if (BootImgHdr->header_version >= BOOT_HEADER_VERSION_THREE) { GUARD_OUT (GetImage (Info, &VendorBootImageBuffer, &VendorBootImageSize, "vendor-boot")); } Status = CheckImageHeader (ImageBuffer, ImageHdrSize, VendorBootImageBuffer, VendorBootImageSize, &ImageSizeActual, &PageSize, Info->BootIntoRecovery); if (Status != EFI_SUCCESS) { DEBUG ((EFI_D_ERROR, "Invalid boot image header:%r\n", Status)); goto out; } if (AllowVerificationError) { Info->BootState = ORANGE; } else { if (UserData->IsUserKey) { Info->BootState = YELLOW; } else { Info->BootState = GREEN; } } /* command line */ GUARD_OUT (AppendVBCommonCmdLine (Info)); GUARD_OUT (AppendVBCmdLine (Info, SlotData->cmdline)); /* Set Rot & Boot State*/ Data.Color = Info->BootState; Data. IsUnlocked = AllowVerificationError; Data.PublicKeyLength = UserData->PublicKeyLen; Data.PublicKey = UserData->PublicKey; GUARD_OUT (KeyMasterGetDateSupport (&DateSupport)); if (BootImgHdr->header_version >= BOOT_HEADER_VERSION_THREE) { OSVersion = ((boot_img_hdr_v3 *)(ImageBuffer))->os_version; } else { OSVersion = BootImgHdr->os_version; } /* Send date value in security patch only when KM TA supports it and the * property is available in vbmeta data, send the old value in other cases */ DEBUG ((EFI_D_VERBOSE, "DateSupport: %d\n", DateSupport)); if (DateSupport) { BootSecurityLevel = avb_property_lookup ( SlotData->vbmeta_images[0].vbmeta_data, SlotData->vbmeta_images[0].vbmeta_size, "com.android.build.boot.security_patch", 0, &BootSecurityLevelSize); if (BootSecurityLevel != NULL && BootSecurityLevelSize == MAX_PROPERTY_SIZE) { Data.SystemSecurityLevel = ParseBootSecurityLevel (BootSecurityLevel, BootSecurityLevelSize); if (Data.SystemSecurityLevel < 0) { DEBUG ((EFI_D_ERROR, "System security patch level format invalid\n")); Status = EFI_INVALID_PARAMETER; goto out; } } else { Data.SystemSecurityLevel = (OSVersion & 0x7FF); } } else { Data.SystemSecurityLevel = (OSVersion & 0x7FF); } Data.SystemVersion = (OSVersion & 0xFFFFF800) >> 11; GUARD_OUT (KeyMasterSetRotAndBootState (&Data)); ComputeVbMetaDigest (SlotData, (CHAR8 *)&Digest); GUARD_OUT (SetVerifiedBootHash ((CONST CHAR8 *)&Digest, sizeof(Digest))); DEBUG ((EFI_D_INFO, "VB2: Authenticate complete! boot state is: %a\n", VbSn[Info->BootState].name)); out: if (Status != EFI_SUCCESS) { if (SlotData != NULL) { avb_slot_verify_data_free (SlotData); } if (Ops != NULL) { AvbOpsFree (Ops); } if (UserData != NULL) { avb_free (UserData); } if (VBData != NULL) { avb_free (VBData); } Info->BootState = RED; if (Info->MultiSlotBoot) { HandleActiveSlotUnbootable (); /* HandleActiveSlotUnbootable should have swapped slots and * reboot the device. If no bootable slot found, enter fastboot */ DEBUG ((EFI_D_WARN, "No bootable slots found enter fastboot mode\n")); } else { DEBUG ((EFI_D_WARN, "Non Multi-slot: Unbootable entering fastboot mode\n")); } } DEBUG ((EFI_D_INFO, "VB2: boot state: %a(%d)\n", VbSn[Info->BootState].name, Info->BootState)); return Status; } STATIC EFI_STATUS DisplayVerifiedBootScreen (BootInfo *Info) { EFI_STATUS Status = EFI_SUCCESS; CHAR8 FfbmStr[FFBM_MODE_BUF_SIZE] = {'\0'}; if (GetAVBVersion () < AVB_1) { return EFI_SUCCESS; } if (!StrnCmp (Info->Pname, L"boot", StrLen (L"boot"))) { Status = GetFfbmCommand (FfbmStr, FFBM_MODE_BUF_SIZE); if (Status != EFI_SUCCESS) { DEBUG ((EFI_D_VERBOSE, "No Ffbm cookie found, ignore: %r\n", Status)); FfbmStr[0] = '\0'; } } DEBUG ((EFI_D_VERBOSE, "Boot State is : %d\n", Info->BootState)); switch (Info->BootState) { case RED: Status = DisplayVerifiedBootMenu (DISPLAY_MENU_RED); if (Status != EFI_SUCCESS) { DEBUG ((EFI_D_INFO, "Your device is corrupt. It can't be trusted and will not boot." "\nYour device will shutdown in 30s\n")); } MicroSecondDelay (30000000); ShutdownDevice (); break; case YELLOW: Status = DisplayVerifiedBootMenu (DISPLAY_MENU_YELLOW); if (Status == EFI_SUCCESS) { WaitForExitKeysDetection (); } else { DEBUG ((EFI_D_INFO, "Your device has loaded a different operating system." "\nWait for 5 seconds before proceeding\n")); MicroSecondDelay (5000000); } break; case ORANGE: if (FfbmStr[0] != '\0' && !TargetBuildVariantUser ()) { DEBUG ((EFI_D_VERBOSE, "Device will boot into FFBM mode\n")); } else { Status = DisplayVerifiedBootMenu (DISPLAY_MENU_ORANGE); if (Status == EFI_SUCCESS) { WaitForExitKeysDetection (); } else { DEBUG ( (EFI_D_INFO, "Device is unlocked, Skipping boot verification\n")); MicroSecondDelay (5000000); } } break; default: break; } /* dm-verity warning */ if ((GetAVBVersion () != AVB_2) && !IsEnforcing () && !Info->BootIntoRecovery) { Status = DisplayVerifiedBootMenu (DISPLAY_MENU_EIO); if (Status == EFI_SUCCESS) { WaitForExitKeysDetection (); } else { DEBUG ((EFI_D_INFO, "The dm-verity is not started in restart mode." \ "\nWait for 30 seconds before proceeding\n")); MicroSecondDelay (30000000); } } return EFI_SUCCESS; } STATIC EFI_STATUS LoadImageAndAuthForLE (BootInfo *Info) { EFI_STATUS Status = EFI_SUCCESS; QcomAsn1x509Protocol *QcomAsn1X509Protocal = NULL; CONST UINT8 *OemCertFile = LeOemCertificate; UINTN OemCertFileLen = sizeof (LeOemCertificate); CERTIFICATE OemCert = {NULL}; UINTN HashSize; UINT8 *ImgHash = NULL; UINTN ImgSize; VB_HASH HashAlgorithm; UINT8 *SigAddr = NULL; UINT32 SigSize = 0; CHAR8 *SystemPath = NULL; UINT32 SystemPathLen = 0; BOOLEAN SecureDevice = FALSE; /*Load image*/ GUARD (VBAllocateCmdLine (Info)); GUARD (VBCommonInit (Info)); GUARD (LoadImageNoAuth (Info)); Status = IsSecureDevice (&SecureDevice); if (Status != EFI_SUCCESS) { DEBUG ((EFI_D_ERROR, "VB: Failed read device state: %r\n", Status)); return Status; } if (!SecureDevice) { if (!TargetBuildVariantUser () ) { DEBUG ((EFI_D_INFO, "VB: verification skipped for debug builds\n")); goto skip_verification; } } /* Initialize Verified Boot*/ device_info_vb_t DevInfo_vb; DevInfo_vb.is_unlocked = IsUnlocked (); DevInfo_vb.is_unlock_critical = IsUnlockCritical (); Status = Info->VbIntf->VBDeviceInit (Info->VbIntf, (device_info_vb_t *)&DevInfo_vb); if (Status != EFI_SUCCESS) { DEBUG ((EFI_D_ERROR, "VB: Error during VBDeviceInit: %r\n", Status)); return Status; } /* Locate QcomAsn1x509Protocol*/ Status = gBS->LocateProtocol (&gEfiQcomASN1X509ProtocolGuid, NULL, (VOID **)&QcomAsn1X509Protocal); if (Status != EFI_SUCCESS) { DEBUG ((EFI_D_ERROR, "VB: Error LocateProtocol " "gEfiQcomASN1X509ProtocolGuid: %r\n", Status)); return Status; } /* Read OEM certificate from the embedded header file */ Status = QcomAsn1X509Protocal->ASN1X509VerifyOEMCertificate (QcomAsn1X509Protocal, OemCertFile, OemCertFileLen, &OemCert); if (Status != EFI_SUCCESS) { DEBUG ((EFI_D_ERROR, "VB: Error during " "ASN1X509VerifyOEMCertificate: %r\n", Status)); return Status; } /*Calculate kernel image hash, SHA256 is used by default*/ HashAlgorithm = VB_SHA256; HashSize = VB_SHA256_SIZE; ImgSize = Info->Images[0].ImageSize; ImgHash = AllocateZeroPool (HashSize); if (ImgHash == NULL) { DEBUG ((EFI_D_ERROR, "kernel image hash buffer allocation failed!\n")); Status = EFI_OUT_OF_RESOURCES; return Status; } Status = LEGetImageHash (QcomAsn1X509Protocal, HashAlgorithm, (UINT8 *)Info->Images[0].ImageBuffer, ImgSize, ImgHash, HashSize); if (Status != EFI_SUCCESS) { DEBUG ((EFI_D_ERROR, "VB: Error during VBGetImageHash: %r\n", Status)); return Status; } SigAddr = (UINT8 *)Info->Images[0].ImageBuffer + ImgSize; SigSize = LE_BOOTIMG_SIG_SIZE; Status = LEVerifyHashWithSignature (QcomAsn1X509Protocal, ImgHash, HashAlgorithm, &OemCert, SigAddr, SigSize); if (Status != EFI_SUCCESS) { DEBUG ((EFI_D_ERROR, "VB: Error during " "LEVBVerifyHashWithSignature: %r\n", Status)); return Status; } DEBUG ((EFI_D_INFO, "VB: LoadImageAndAuthForLE complete!\n")); skip_verification: if (!IsRootCmdLineUpdated (Info)) { SystemPathLen = GetSystemPath (&SystemPath, Info->MultiSlotBoot, Info->BootIntoRecovery, (CHAR16 *)L"system", (CHAR8 *)"root"); if (SystemPathLen == 0 || SystemPath == NULL) { return EFI_LOAD_ERROR; } GUARD (AppendVBCmdLine (Info, SystemPath)); } if(IsSecureBootEnabled ()){ GUARD (AppendVBCmdLine (Info, KeymasterLoadState)); } return Status; } EFI_STATUS LoadImageAndAuth (BootInfo *Info) { EFI_STATUS Status = EFI_SUCCESS; BOOLEAN MdtpActive = FALSE; QCOM_MDTP_PROTOCOL *MdtpProtocol; UINT32 AVBVersion = NO_AVB; if (Info == NULL) { DEBUG ((EFI_D_ERROR, "Invalid parameter Info\n")); return EFI_INVALID_PARAMETER; } /* Get Partition Name*/ if (!Info->MultiSlotBoot) { if (Info->BootIntoRecovery) { DEBUG ((EFI_D_INFO, "Booting Into Recovery Mode\n")); StrnCpyS (Info->Pname, ARRAY_SIZE (Info->Pname), L"recovery", StrLen (L"recovery")); } else { DEBUG ((EFI_D_INFO, "Booting Into Mission Mode\n")); StrnCpyS (Info->Pname, ARRAY_SIZE (Info->Pname), L"boot", StrLen (L"boot")); } } else { Slot CurrentSlot = {{0}}; GUARD (FindBootableSlot (&CurrentSlot)); if (IsSuffixEmpty (&CurrentSlot)) { DEBUG ((EFI_D_ERROR, "No bootable slot\n")); return EFI_LOAD_ERROR; } if (IsDynamicPartitionSupport () && Info->BootIntoRecovery) { DEBUG ((EFI_D_INFO, "Booting Into Recovery Mode\n")); StrnCpyS (Info->Pname, ARRAY_SIZE (Info->Pname), L"recovery", StrLen (L"recovery")); } else { DEBUG ((EFI_D_INFO, "Booting Into Mission Mode\n")); GUARD (StrnCpyS (Info->Pname, ARRAY_SIZE (Info->Pname), L"boot", StrLen (L"boot"))); } GUARD (StrnCatS (Info->Pname, ARRAY_SIZE (Info->Pname), CurrentSlot.Suffix, StrLen (CurrentSlot.Suffix))); } DEBUG ((EFI_D_VERBOSE, "MultiSlot %a, partition name %s\n", BooleanString[Info->MultiSlotBoot].name, Info->Pname)); if (FixedPcdGetBool (EnableMdtpSupport)) { Status = IsMdtpActive (&MdtpActive); if (EFI_ERROR (Status)) { DEBUG ((EFI_D_ERROR, "Failed to get activation state for MDTP, " "Status=%r." " Considering MDTP as active and continuing \n", Status)); if (Status != EFI_NOT_FOUND) MdtpActive = TRUE; } } AVBVersion = GetAVBVersion (); DEBUG ((EFI_D_VERBOSE, "AVB version %d\n", AVBVersion)); /* Load and Authenticate */ switch (AVBVersion) { case NO_AVB: return LoadImageNoAuthWrapper (Info); break; case AVB_1: Status = LoadImageAndAuthVB1 (Info); break; case AVB_2: Status = LoadImageAndAuthVB2 (Info); break; case AVB_LE: Status = LoadImageAndAuthForLE (Info); break; default: DEBUG ((EFI_D_ERROR, "Unsupported AVB version %d\n", AVBVersion)); Status = EFI_UNSUPPORTED; } // if MDTP is active Display Recovery UI if (Status != EFI_SUCCESS && MdtpActive) { Status = gBS->LocateProtocol (&gQcomMdtpProtocolGuid, NULL, (VOID **)&MdtpProtocol); if (EFI_ERROR (Status)) { DEBUG ( (EFI_D_ERROR, "Failed to locate MDTP protocol, Status=%r\n", Status)); return Status; } /* Perform Local Deactivation of MDTP */ Status = MdtpProtocol->MdtpDeactivate (MdtpProtocol, FALSE); } if (IsUnlocked () && Status != EFI_SUCCESS) { DEBUG ((EFI_D_ERROR, "LoadImageAndAuth failed %r\n", Status)); return Status; } if (AVBVersion != AVB_LE) { DisplayVerifiedBootScreen (Info); DEBUG ((EFI_D_VERBOSE, "Sending Milestone Call\n")); Status = Info->VbIntf->VBSendMilestone (Info->VbIntf); if (Status != EFI_SUCCESS) { DEBUG ((EFI_D_ERROR, "Error sending milestone call to TZ\n")); return Status; } } return Status; } VOID FreeVerifiedBootResource (BootInfo *Info) { DEBUG ((EFI_D_VERBOSE, "FreeVerifiedBootResource\n")); if (Info == NULL) { return; } VB2Data *VBData = Info->VBData; if (VBData != NULL) { AvbOps *Ops = VBData->Ops; if (Ops != NULL) { if (Ops->user_data != NULL) { avb_free (Ops->user_data); } AvbOpsFree (Ops); } AvbSlotVerifyData *SlotData = VBData->SlotData; if (SlotData != NULL) { avb_slot_verify_data_free (SlotData); } avb_free (VBData); } if (Info->VBCmdLine != NULL) { FreePool (Info->VBCmdLine); } return; } EFI_STATUS GetCertFingerPrint (UINT8 *FingerPrint, UINTN FingerPrintLen, UINTN *FingerPrintLenOut) { EFI_STATUS Status = EFI_SUCCESS; if (FingerPrint == NULL || FingerPrintLenOut == NULL || FingerPrintLen < AVB_SHA256_DIGEST_SIZE) { DEBUG ((EFI_D_ERROR, "GetCertFingerPrint: Invalid parameters\n")); return EFI_INVALID_PARAMETER; } if (GetAVBVersion () == AVB_1) { QCOM_VERIFIEDBOOT_PROTOCOL *VbIntf = NULL; Status = gBS->LocateProtocol (&gEfiQcomVerifiedBootProtocolGuid, NULL, (VOID **)&VbIntf); if (Status != EFI_SUCCESS) { DEBUG ((EFI_D_ERROR, "Unable to locate VerifiedBoot Protocol\n")); return Status; } if (VbIntf->Revision < QCOM_VERIFIEDBOOT_PROTOCOL_REVISION) { DEBUG ((EFI_D_ERROR, "GetCertFingerPrint: VB1: not " "supported for this revision\n")); return EFI_UNSUPPORTED; } Status = VbIntf->VBGetCertFingerPrint (VbIntf, FingerPrint, FingerPrintLen, FingerPrintLenOut); if (Status != EFI_SUCCESS) { DEBUG ((EFI_D_ERROR, "Failed Reading CERT FingerPrint\n")); return Status; } } else if (GetAVBVersion () == AVB_2) { CHAR8 *UserKeyBuffer = NULL; UINT32 UserKeyLength = 0; AvbSHA256Ctx UserKeyCtx = {{0}}; CHAR8 *UserKeyDigest = NULL; GUARD (GetUserKey (&UserKeyBuffer, &UserKeyLength)); avb_sha256_init (&UserKeyCtx); avb_sha256_update (&UserKeyCtx, (UINT8 *)UserKeyBuffer, UserKeyLength); UserKeyDigest = (CHAR8 *)avb_sha256_final (&UserKeyCtx); CopyMem (FingerPrint, UserKeyDigest, AVB_SHA256_DIGEST_SIZE); *FingerPrintLenOut = AVB_SHA256_DIGEST_SIZE; } else { DEBUG ((EFI_D_ERROR, "GetCertFingerPrint: not supported\n")); return EFI_UNSUPPORTED; } return Status; }