// // This file contains a 'Sample Driver' and is licensed as such // under the terms of your license agreement with Intel or your // vendor. This file may be modified by the user, subject to // the additional terms of the license agreement // /** Utility to enroll UEFI value, including PK, KEK, db/dbx/dbt at build time. Copyright (c) 2014 - 2020, Intel Corporation. All rights reserved.
This software and associated documentation (if any) is furnished under a license and may only be used or copied in accordance with the terms of the license. Except as permitted by such license, no part of this software or documentation may be reproduced, stored in a retrieval system, or transmitted in any form or by any means without the express written consent of Intel Corporation. **/ #include "KeyEnroll.h" #include "VariableCommon.h" #include "EfiUtilityMsgs.c" #include "AuthVariable.h" #include EFI_GUID gEfiGlobalVariableGuid = EFI_GLOBAL_VARIABLE; EFI_GUID gEfiImageSecurityDatabaseGuid = EFI_IMAGE_SECURITY_DATABASE_GUID; EFI_GUID gEfiSystemNvDataFvGuid = EFI_SYSTEM_NV_DATA_FV_GUID; EFI_GUID gEfiCertRsa2048Guid = EFI_CERT_RSA2048_GUID; EFI_GUID gEfiCertX509Guid = EFI_CERT_X509_GUID; EFI_GUID gEfiCertSha1Guid = EFI_CERT_SHA1_GUID; EFI_GUID gEfiCertSha256Guid = EFI_CERT_SHA256_GUID; EFI_GUID gEfiCertSha384Guid = EFI_CERT_SHA384_GUID; EFI_GUID gEfiCertSha512Guid = EFI_CERT_SHA512_GUID; EFI_GUID gEfiCertTypeRsa2048Sha256Guid = EFI_CERT_TYPE_RSA2048_SHA256_GUID; EFI_GUID gEfiCertTypePkcs7Guid = EFI_CERT_TYPE_PKCS7_GUID; EFI_GUID gEfiCertX509Sha256Guid = EFI_CERT_X509_SHA256_GUID; EFI_GUID gEfiCertX509Sha384Guid = EFI_CERT_X509_SHA384_GUID; EFI_GUID gEfiCertX509Sha512Guid = EFI_CERT_X509_SHA512_GUID; // // OID ASN.1 Value for Hash Algorithms // UINT8 mHashOidValueSha1[] = {0x2B, 0x0E, 0x03, 0x02, 0x1A}; UINT8 mHashOidValueSha256[] = {0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01}; UINT8 mHashOidValueSha384[] = {0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x02}; UINT8 mHashOidValueSha512[] = {0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03}; HASH_TABLE mHash[] = { { L"SHA1", SHA1_DIGEST_SIZE, mHashOidValueSha1, sizeof(mHashOidValueSha1)}, { L"SHA256", SHA256_DIGEST_SIZE, mHashOidValueSha256, sizeof(mHashOidValueSha256)}, { L"SHA384", SHA384_DIGEST_SIZE, mHashOidValueSha384, sizeof(mHashOidValueSha384)}, { L"SHA512", SHA512_DIGEST_SIZE, mHashOidValueSha512, sizeof(mHashOidValueSha512)}, }; G_EFI_FD_INFO gEfiFdInfo; LIST_ENTRY mVarListEntry; UINT8 mIgnoreTimeStamp; unsigned int xtoi ( char *str ); VOID PrintUtilityInfo ( VOID ) /*++ Routine Description: Displays the standard utility information to SDTOUT Arguments: None Returns: None --*/ { printf ( "%s - Tiano KeyEnroll Utility."" Version %i.%i\n\n", UTILITY_NAME, UTILITY_MAJOR_VERSION, UTILITY_MINOR_VERSION ); } VOID PrintUsage ( VOID ) /*++ Routine Description: Displays the utility usage syntax to STDOUT Arguments: None Returns: None --*/ { printf ("Usage: %s InputFile [OutputFile] [Command] [SubCommand] [Options [Parameter(s)]]\n" "\tCommand: PK|KEK|db|dbx|VAR|INFO\n" "\tPK ADD|DEL [-F [-G ]]\n" "\tKEK ADD|APPEND|DEL [-F |-K -G ]\n" "\tdb|dbx ADD|APPEND|DEL [-F |-I -G ]\n" "\tdbt ADD|APPEND|DEL [-F -G ]\n" "\tdbx ADD|APPEND -F -G -H SHA256|SHA384|SHA512 [-DATE |NOW -TIME |NOW]]\n" "\tVAR ADD|APPEND|DEL -N -G [-A -D [-T]]\n" "\tINFO [PK|KEK|db|dbx|dbt|ALL]\n" "", UTILITY_NAME); printf (" Where:\n"); printf ("\tInputFile - Name of the input FD file.\n"); printf ("\tOutputFile - Name of the output FD file. No such parameter for INFO command.\n"); printf ("\tCertFile - DER-encoded certificate.\n"); printf ("\tKeyFile - Public key binary file.\n"); printf ("\tImageFile - UEFI PE/COFF image file.\n"); printf ("\tDataFile - The raw bytes passed in *Data to SetVariable()... \n"); printf ("\t If -T is also specified, it includes an EFI_VARIABLE_AUTHENTICATION_2\n"); printf ("\t concatenated with the new variable value for time based Auth Variable.\n"); } /** Compare two EFI_TIME data. @param FirstTime A pointer to the first EFI_TIME data. @param SecondTime A pointer to the second EFI_TIME data. @retval TRUE The FirstTime is not later than the SecondTime. @retval FALSE The FirstTime is later than the SecondTime. **/ BOOLEAN CompareTimeStamp ( IN EFI_TIME *FirstTime, IN EFI_TIME *SecondTime ) { if (FirstTime->Year != SecondTime->Year) { return (BOOLEAN) (FirstTime->Year < SecondTime->Year); } else if (FirstTime->Month != SecondTime->Month) { return (BOOLEAN) (FirstTime->Month < SecondTime->Month); } else if (FirstTime->Day != SecondTime->Day) { return (BOOLEAN) (FirstTime->Day < SecondTime->Day); } else if (FirstTime->Hour != SecondTime->Hour) { return (BOOLEAN) (FirstTime->Hour < SecondTime->Hour); } else if (FirstTime->Minute != SecondTime->Minute) { return (BOOLEAN) (FirstTime->Minute < SecondTime->Minute); } return (BOOLEAN) (FirstTime->Second <= SecondTime->Second); } VOID GetCurrentTime ( OUT EFI_TIME *TimeStamp ) { time_t CurTime; struct tm *GmTime; memset (TimeStamp, 0, sizeof (*TimeStamp)); if (mIgnoreTimeStamp == 1) { return; } time (&CurTime); GmTime = gmtime (&CurTime); if (GmTime == NULL) { return; } TimeStamp->Year = (UINT16)GmTime->tm_year + 1900; // Year is 1900-based TimeStamp->Month = (UINT8)GmTime->tm_mon + 1; // Month is zero based. TimeStamp->Day = (UINT8)GmTime->tm_mday; TimeStamp->Hour = (UINT8)GmTime->tm_hour; TimeStamp->Minute = (UINT8)GmTime->tm_min; TimeStamp->Second = (UINT8)GmTime->tm_sec; } BOOLEAN IsGuidData ( IN CHAR8 *StringData, IN EFI_GUID *Guid ) /*++ Routine Description: Check whether a string is a GUID Arguments: StringData - the String Guid - Guid to hold the value Returns: TRUE - StringData is a GUID, and Guid field is filled. FALSE - StringData is not a GUID --*/ { if (strlen (StringData) != strlen ("00000000-0000-0000-0000-000000000000")) { return FALSE; } if ((StringData[8] != '-') || (StringData[13] != '-') || (StringData[18] != '-') || (StringData[23] != '-') ) { return FALSE; } StringToGuid (StringData, Guid); return TRUE; } BOOLEAN IsDateData ( IN CHAR8 *StringData, IN EFI_TIME *Time ) { UINTN Index; UINT32 Month; UINT32 Day; UINT32 Year; EFI_TIME TempTime; if ((strcmp (StringData, "NOW") == 0) || (strcmp (StringData, "now") == 0)) { // // Fetch the current time based on UTC timezone // GetCurrentTime (&TempTime); Time->Month = TempTime.Month; Time->Day = TempTime.Day; Time->Year = TempTime.Year; return TRUE; } if (strlen (StringData) != strlen ("MM/DD/YYYY")) { return FALSE; } if ((StringData[2] != '/') || (StringData[5] != '/') ) { return FALSE; } for (Index = 0; Index < 10; Index++) { if ((Index == 2) || (Index == 5)) { continue; } if ((StringData[Index] >= '0') && (StringData[Index] <= '9')) { continue; } else { return FALSE; } } Index = sscanf ( StringData, "%02u/%02u/%04u", &Month, &Day, &Year ); if (Index != 3) { return FALSE; } if ((Month < 1) || (Month > 12)) { return FALSE; } if ((Day < 1) || (Day > 31)) { return FALSE; } if ((Year < 1900) || (Year > 9999)) { return FALSE; } Time->Month = (UINT8)Month; Time->Day = (UINT8)Day; Time->Year = (UINT16)Year; return TRUE; } BOOLEAN IsTimeData ( IN CHAR8 *StringData, IN EFI_TIME *Time ) { UINTN Index; UINT32 Hour; UINT32 Minute; UINT32 Second; EFI_TIME TempTime; if ((strcmp (StringData, "NOW") == 0) || (strcmp (StringData, "now") == 0)) { // // Fetch the current time based on UTC timezone // GetCurrentTime (&TempTime); Time->Hour = TempTime.Hour; Time->Minute = TempTime.Minute; Time->Second = TempTime.Second; return TRUE; } if (strlen (StringData) != strlen ("HH:MM:SS")) { return FALSE; } if ((StringData[2] != ':') || (StringData[5] != ':') ) { return FALSE; } for (Index = 0; Index < 8; Index++) { if ((Index == 2) || (Index == 5)) { continue; } if ((StringData[Index] >= '0') && (StringData[Index] <= '9')) { continue; } else { return FALSE; } } Index = sscanf ( StringData, "%02u:%02u:%02u", &Hour, &Minute, &Second ); if (Index != 3) { return FALSE; } if (Hour > 23) { return FALSE; } if (Minute > 59) { return FALSE; } if (Second > 59) { return FALSE; } Time->Hour = (UINT8)Hour; Time->Minute = (UINT8)Minute; Time->Second = (UINT8)Second; return TRUE; } /** Retrieve the TBSCertificate from one given X.509 certificate. @param[in] Cert Pointer to the given DER-encoded X509 certificate. @param[in] CertSize Size of the X509 certificate in bytes. @param[out] TBSCert DER-Encoded To-Be-Signed certificate. @param[out] TBSCertSize Size of the TBS certificate in bytes. If Cert is NULL, then return FALSE. If TBSCert is NULL, then return FALSE. If TBSCertSize is NULL, then return FALSE. @retval TRUE The TBSCertificate was retrieved successfully. @retval FALSE Invalid X.509 certificate. **/ BOOLEAN EFIAPI X509GetTBSCert ( IN CONST UINT8 *Cert, IN UINTN CertSize, OUT UINT8 **TBSCert, OUT UINTN *TBSCertSize ) { CONST UINT8 *Temp; INTN Asn1Tag; INTN ObjClass; UINTN Length; // // Check input parameters. // if ((Cert == NULL) || (TBSCert == NULL) || (TBSCertSize == NULL)) { return FALSE; } // // An X.509 Certificate is: (defined in RFC3280) // Certificate ::= SEQUENCE { // tbsCertificate TBSCertificate, // signatureAlgorithm AlgorithmIdentifier, // signature BIT STRING } // // and // // TBSCertificate ::= SEQUENCE { // version [0] Version DEFAULT v1, // ... // } // // So we can just ASN1-parse the x.509 DER-encoded data. If we strip // the first SEQUENCE, the second SEQUENCE is the TBSCertificate. // Temp = Cert; ASN1_get_object (&Temp, (long *)&Length, (int *)&Asn1Tag, (int *)&ObjClass, (long)CertSize); if (Asn1Tag != V_ASN1_SEQUENCE) { return FALSE; } *TBSCert = (UINT8 *)Temp; ASN1_get_object (&Temp, (long *)&Length, (int *)&Asn1Tag, (int *)&ObjClass, (long)Length); // // Verify the parsed TBSCertificate is one correct SEQUENCE data. // if (Asn1Tag != V_ASN1_SEQUENCE) { return FALSE; } *TBSCertSize = Length + (Temp - *TBSCert); return TRUE; } VOID DumpHex ( IN UINT8 *Buffer, IN UINTN BufferSize ) /*++ Routine Description: Dump hex data Arguments: Buffer - Buffer address BufferSize - Buffer size Returns: None --*/ { UINTN Index; UINTN IndexJ; #define COL_SIZE 16 for (Index = 0; Index < BufferSize/COL_SIZE; Index++) { printf (" %04x: ", (UINT16) Index * COL_SIZE); for (IndexJ = 0; IndexJ < COL_SIZE; IndexJ++) { printf ("%02x ", *(Buffer + Index * COL_SIZE + IndexJ)); } printf ("\n"); } if ((BufferSize % COL_SIZE) != 0) { printf (" %04x: ", (UINT16) Index * COL_SIZE); for (IndexJ = 0; IndexJ < (BufferSize % COL_SIZE); IndexJ++) { printf ("%02x ", *(Buffer + Index * COL_SIZE + IndexJ)); } printf ("\n"); } } VOID Unicode2AsciiString ( IN CHAR16 *UniString, OUT CHAR8 *String ) /*++ Routine Description: Write ascii string as unicode string format to FILE Arguments: String - Pointer to string that is written to FILE. UniString - Pointer to unicode string Returns: NULL --*/ { while (*UniString != '\0') { *(String++) = (CHAR8)*(UniString++); } *String = '\0'; } VOID Ascii2UnicodeString ( IN CHAR8 *String, OUT CHAR16 *UniString ) /*++ Routine Description: Write ascii string as unicode string format to FILE Arguments: String - Pointer to string that is written to FILE. UniString - Pointer to unicode string Returns: NULL --*/ { while (*String != '\0') { *(UniString++) = (CHAR16)*(String++); } *UniString = '\0'; } STATUS ReadInputFile ( IN CHAR8 *FileName, OUT UINT8 **FileData, OUT UINT32 *FileSize, OUT UINT8 **FileBufferRaw OPTIONAL ) /*++ Routine Description: Read input file Arguments: FileName - The input file name FileData - The input file data, the memory is alligned. FileSize - The input file size FileBufferRaw - The memory to hold input file data. The caller should use this to free the memory. Returns: STATUS_SUCCESS - The file found and data read STATUS_ERROR - The file data is not read STATUS_WARNING - The file is not found --*/ { FILE *FpIn; UINT32 TempResult; // // Open the Input Fvrecovery.fv file // if ((FpIn = fopen (FileName, "rb")) == NULL) { // // Return WARNING, let caller make decision // // Error (NULL, 0, 0, "Unable to open file", FileName); return STATUS_WARNING; } // // Get the Input Fvrecovery.fv file size // fseek (FpIn, 0, SEEK_END); *FileSize = ftell (FpIn); // // Read the contents of input file to memory buffer // if (FileBufferRaw != NULL) { *FileBufferRaw = (UINT8 *) malloc (*FileSize + 0x10000); if (NULL == *FileBufferRaw) { Error (NULL, 0, 0, "No sufficient memory to allocate!", NULL); fclose (FpIn); return STATUS_ERROR; } TempResult = 0x10000 - (UINT32) ((UINTN) *FileBufferRaw & 0x0FFFF); *FileData = (UINT8 *)((UINTN)*FileBufferRaw + TempResult); } else { *FileData = (UINT8 *) malloc (*FileSize); if (NULL == *FileData) { Error (NULL, 0, 0, "No sufficient memory to allocate!", NULL); fclose (FpIn); return STATUS_ERROR; } } fseek (FpIn, 0, SEEK_SET); TempResult = fread (*FileData, 1, *FileSize, FpIn); if (TempResult != *FileSize) { Error (NULL, 0, 0, "Read input file error!", NULL); if (FileBufferRaw != NULL) { free ((VOID *)*FileBufferRaw); } else { free ((VOID *)*FileData); } fclose (FpIn); return STATUS_ERROR; } // // Close the input Fvrecovery.fv file // fclose (FpIn); return STATUS_SUCCESS; } STATUS WriteOutputFile ( IN CHAR8 *FileName, IN UINT8 *FileData, IN UINT32 FileSize ) /*++ Routine Description: Read input file Arguments: FileName - The input file name FileData - The input file data FileSize - The input file size Returns: STATUS_SUCCESS - Write file data successfully STATUS_ERROR - The file data is not written --*/ { FILE *FpOut; // // Open the output Fvrecovery.fv file // if ((FpOut = fopen (FileName, "w+b")) == NULL) { Error (NULL, 0, 0, "Unable to open file", FileName); return STATUS_ERROR; } // // Write the output Fvrecovery.fv file // if ((fwrite (FileData, 1, FileSize, FpOut)) != FileSize) { Error (NULL, 0, 0, "Write output file error!", NULL); fclose (FpOut); return STATUS_ERROR; } // // Close the output Fvrecovery.fv file // fclose (FpOut); return STATUS_SUCCESS; } UINT8 * FindNextFvHeader ( IN UINT8 *FileBuffer, IN UINTN FileLength ) /*++ Routine Description: Find next FvHeader in the FileBuffer Parameters: FileBuffer - The start FileBuffer which needs to be searched FileLength - The whole File Length. Return: FvHeader - The FvHeader is found successfully. NULL - The FvHeader is not found. --*/ { UINT8 *FileHeader; EFI_FIRMWARE_VOLUME_HEADER *FvHeader; UINT16 FileChecksum; FileHeader = FileBuffer; for (; (UINTN)FileBuffer < (UINTN)FileHeader + FileLength; FileBuffer += 8) { FvHeader = (EFI_FIRMWARE_VOLUME_HEADER *)FileBuffer; if (FvHeader->Signature == EFI_FVH_SIGNATURE) { // // potential candidate // // // Check checksum // if (FvHeader->FvLength > FileLength) { continue; } if (FvHeader->HeaderLength >= FileLength) { continue; } FileChecksum = CalculateChecksum16 ((UINT16 *)FileBuffer, FvHeader->HeaderLength / sizeof (UINT16)); if (FileChecksum != 0) { continue; } // // Check revision and reserved field // if ((FvHeader->Revision == EFI_FVH_PI_REVISION) && (FvHeader->Reserved[0] == 0) ){ return FileHeader; } } } return NULL; } UINT32 GetVariableInfoFromFd ( IN UINT8 *FdBuffer, IN UINT32 FdFileSize, OUT UINT8 **VariableFv ) /*++ Routine Description: Get Variable information from Fd file. Assume Fv region is continuous in Fd file. Arguments: FdBuffer - Fd file buffer. FdFileSize - Fd file size. Fvrecovery - Fvrecovery pointer in Fd file buffer Returns: Fvrecovery file size --*/ { UINT8 *FileBuffer; UINT32 VariableFvFileSize; FileBuffer = FindNextFvHeader (FdBuffer, FdFileSize); if (FileBuffer == NULL) { *VariableFv = NULL; return 0; } VariableFvFileSize = 0; *VariableFv = NULL; while ((UINTN)FileBuffer < (UINTN)FdBuffer + FdFileSize) { // // Check variable // if (((EFI_FIRMWARE_VOLUME_HEADER *)FileBuffer)->Signature != EFI_FVH_SIGNATURE) { FileBuffer += 0x10; continue; } if (CompareGuid (&gEfiSystemNvDataFvGuid, &((EFI_FIRMWARE_VOLUME_HEADER *)FileBuffer)->FileSystemGuid) == 0) { VariableFvFileSize = (UINT32)((EFI_FIRMWARE_VOLUME_HEADER *)FileBuffer)->FvLength; *VariableFv = FileBuffer; break; } // // Next fv // FileBuffer = FindNextFvHeader (FileBuffer, (UINTN)FdBuffer + FdFileSize - (UINTN)FileBuffer); if (FileBuffer == NULL) { break; } FileBuffer = (UINT8 *)FileBuffer + ((EFI_FIRMWARE_VOLUME_HEADER *)FileBuffer)->FvLength; } return VariableFvFileSize; } /** Exchange the data between Efi variable and the data of VarList If VarToList is TRUE, copy the efi variable data to the VarList; Otherwise, update the data from varlist to efi variable. @param VarToList The flag to control the direction of exchange. @param StorageListHead Decide which variale list be updated @retval EFI_SUCCESS Get the address successfully. **/ EFI_STATUS EfiVarAndListExchange ( IN BOOLEAN VarToList, IN LIST_ENTRY *StorageListHead ) { EFI_FIRMWARE_VOLUME_HEADER *VarAddr; EFI_STATUS Status; VOID *VariableStoreHeader; VARIABLE_TYPE VariableType; Status = EFI_ABORTED; VarAddr = (EFI_FIRMWARE_VOLUME_HEADER *) gEfiFdInfo.EfiVariableAddr; VariableStoreHeader = (VOID *)((CHAR8 *)VarAddr + VarAddr->HeaderLength); // // Judge the layout of NV by gEfiVariableGuid // VariableType = CheckVarStore (VariableStoreHeader); Status = SynEfiVariable (VarToList, VariableType, StorageListHead); return Status; } /** Free resources of a storage. @param Storage Pointer of the storage **/ VOID DestroyStorage ( IN VARIABLE_INFO_PRIVATE *Storage ) { if (Storage == NULL) { return; } if (Storage->Name != NULL) { free (Storage->Name); } if (Storage->Buffer != NULL) { free (Storage->Buffer); } free (Storage); Storage = NULL; } /** Free resources allocated for all Storage in an LIST_ENTRY. @param FormSet Pointer of the FormSet **/ VOID DestroyAllStorage ( IN LIST_ENTRY *StorageListHead ) { LIST_ENTRY *Link; VARIABLE_INFO_PRIVATE *Storage; if (StorageListHead->ForwardLink != NULL) { while (!IsListEmpty (StorageListHead)) { Link = GetFirstNode (StorageListHead); Storage = VARIABLE_INFO_PRIVATE_FROM_LINK (Link); RemoveEntryList (&Storage->Link); DestroyStorage (Storage); } } StorageListHead = NULL; } LIST_ENTRY * FindVariableList ( IN LIST_ENTRY *StorageListHead, IN CHAR16 *Name, IN EFI_GUID *Guid ) { LIST_ENTRY *Link; VARIABLE_INFO_PRIVATE *Storage; if (StorageListHead->ForwardLink != NULL) { Link = GetFirstNode (StorageListHead); while (!IsNull (StorageListHead, Link)) { Storage = VARIABLE_INFO_PRIVATE_FROM_LINK (Link); if ((CompareGuid (&Storage->Guid, Guid) == 0) && (KeyEnrollStrCmp (Storage->Name, Name) == 0)) { return Link; } Link = GetNextNode (StorageListHead, Link); } } return NULL; } /* Find out the matching VARIABLE_INFO_PRIVATE by variable name and guid. @param[in] VariableName Name of variable to be find. @param[in] VendorGuid Variable vendor GUID. @retval VARIABLE_INFO_PRIVATE A pointer to VARIABLE_INFO_PRIVATE. @retval NULL Not find. */ VARIABLE_INFO_PRIVATE* FindVariableInfoPtr( IN CHAR16 *VariableName, IN EFI_GUID *VendorGuid ) { LIST_ENTRY *ListEntry; // // Try to get auth variable by name and GUID. // ListEntry = FindVariableList (&mVarListEntry, VariableName, VendorGuid); if (ListEntry != NULL) { return VARIABLE_INFO_PRIVATE_FROM_LINK (ListEntry); } return NULL; } /** Filter out the duplicated EFI_SIGNATURE_DATA from the new data by comparing to the original data. And copy the combined data to final data. @param[in] Data Pointer to original EFI_SIGNATURE_LIST. @param[in] DataSize Size of Data buffer. @param[in] NewData Pointer to new EFI_SIGNATURE_LIST. @param[in] NewDataSize Size of NewData buffer. @param[out] FinalData Pointer to final combined EFI_SIGNATURE_LIST. @param[out] FinalDataSize Size of FinalData buffer. **/ VOID FilterSignatureList ( IN VOID *Data, IN UINTN DataSize, IN VOID *NewData, IN UINTN NewDataSize, OUT VOID *FinalData, OUT UINTN *FinalDataSize ) { EFI_SIGNATURE_LIST *CertList; EFI_SIGNATURE_DATA *Cert; UINTN CertCount; EFI_SIGNATURE_LIST *NewCertList; EFI_SIGNATURE_DATA *NewCert; UINTN NewCertCount; UINTN Index; UINTN Index2; UINTN NewSize; UINTN Size; UINT8 *Tail; UINTN CopiedCount; UINTN SignatureListSize; BOOLEAN IsNewCert; // // Copy the original data to final data first. // CopyMem (FinalData, Data, DataSize); *FinalDataSize = DataSize; if (NewDataSize == 0) { return; } Tail = (UINT8 *) FinalData + *FinalDataSize; NewSize = NewDataSize; NewCertList = (EFI_SIGNATURE_LIST *) NewData; while ((NewSize > 0) && (NewSize >= NewCertList->SignatureListSize)) { NewCert = (EFI_SIGNATURE_DATA *) ((UINT8 *) NewCertList + sizeof (EFI_SIGNATURE_LIST) + NewCertList->SignatureHeaderSize); NewCertCount = (NewCertList->SignatureListSize - sizeof (EFI_SIGNATURE_LIST) - NewCertList->SignatureHeaderSize) / NewCertList->SignatureSize; CopiedCount = 0; for (Index = 0; Index < NewCertCount; Index++) { IsNewCert = TRUE; Size = DataSize; CertList = (EFI_SIGNATURE_LIST *) Data; while ((Size > 0) && (Size >= CertList->SignatureListSize)) { if ((CompareGuid (&CertList->SignatureType, &NewCertList->SignatureType) == 0) && (CertList->SignatureSize == NewCertList->SignatureSize)) { Cert = (EFI_SIGNATURE_DATA *) ((UINT8 *) CertList + sizeof (EFI_SIGNATURE_LIST) + CertList->SignatureHeaderSize); CertCount = (CertList->SignatureListSize - sizeof (EFI_SIGNATURE_LIST) - CertList->SignatureHeaderSize) / CertList->SignatureSize; for (Index2 = 0; Index2 < CertCount; Index2++) { // // Iterate each Signature Data in this Signature List. // if (CompareMem (NewCert, Cert, CertList->SignatureSize) == 0) { IsNewCert = FALSE; break; } Cert = (EFI_SIGNATURE_DATA *) ((UINT8 *) Cert + CertList->SignatureSize); } } if (!IsNewCert) { break; } Size -= CertList->SignatureListSize; CertList = (EFI_SIGNATURE_LIST *) ((UINT8 *) CertList + CertList->SignatureListSize); } if (IsNewCert) { printf ("found new cert!\n"); // // New EFI_SIGNATURE_DATA, keep it. // if (CopiedCount == 0) { // // Copy EFI_SIGNATURE_LIST header for only once. // CopyMem (Tail, NewCertList, sizeof (EFI_SIGNATURE_LIST) + NewCertList->SignatureHeaderSize); Tail = Tail + sizeof (EFI_SIGNATURE_LIST) + NewCertList->SignatureHeaderSize; } CopyMem (Tail, NewCert, NewCertList->SignatureSize); Tail += NewCertList->SignatureSize; CopiedCount++; } NewCert = (EFI_SIGNATURE_DATA *) ((UINT8 *) NewCert + NewCertList->SignatureSize); } // // Update SignatureListSize in the kept EFI_SIGNATURE_LIST. // if (CopiedCount != 0) { SignatureListSize = sizeof (EFI_SIGNATURE_LIST) + NewCertList->SignatureHeaderSize + (CopiedCount * NewCertList->SignatureSize); CertList = (EFI_SIGNATURE_LIST *) (Tail - SignatureListSize); CertList->SignatureListSize = (UINT32) SignatureListSize; } NewSize -= NewCertList->SignatureListSize; NewCertList = (EFI_SIGNATURE_LIST *) ((UINT8 *) NewCertList + NewCertList->SignatureListSize); } *FinalDataSize = (Tail - (UINT8 *) FinalData); } EFI_STATUS UpdateVariableList ( IN LIST_ENTRY *Link, IN CHAR16 *Name, IN EFI_GUID *Guid, IN UINT32 Attributes, IN EFI_TIME *TimeStamp, IN UINTN Size, IN UINT8 *Buffer, IN BOOLEAN Append ) { VARIABLE_INFO_PRIVATE *Storage; UINTN NewSize; UINT8 *NewBuffer; Storage = VARIABLE_INFO_PRIVATE_FROM_LINK (Link); Storage->Attributes = Attributes; if (Append) { if ((Attributes & EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS) != 0) { // // When the new TimeStamp value is later than the current timestamp associated // with the variable, we need associate the new timestamp with the updated value. // if (CompareTimeStamp (&Storage->TimeStamp, TimeStamp)) { CopyMem (&Storage->TimeStamp, TimeStamp, sizeof (*TimeStamp)); } } NewSize = Storage->Size + Size; NewBuffer = malloc (NewSize); if (NewBuffer == NULL) { Error (NULL, 0, 0, "Unable to allocate memory", NULL); return EFI_OUT_OF_RESOURCES; } ASSERT (Storage->Buffer != NULL); if (((CompareGuid (Guid, &gEfiImageSecurityDatabaseGuid) == 0) && ((KeyEnrollStrCmp (Name, EFI_IMAGE_SECURITY_DATABASE) == 0) || (KeyEnrollStrCmp (Name, EFI_IMAGE_SECURITY_DATABASE1) == 0) || (KeyEnrollStrCmp (Name, EFI_IMAGE_SECURITY_DATABASE2) == 0))) || ((CompareGuid (Guid, &gEfiGlobalVariableGuid) == 0) && (KeyEnrollStrCmp (Name, EFI_KEY_EXCHANGE_KEY_NAME) == 0))) { FilterSignatureList ( Storage->Buffer, Storage->Size, Buffer, Size, NewBuffer, &NewSize ); } else { CopyMem (NewBuffer, Storage->Buffer, Storage->Size); CopyMem (NewBuffer + Storage->Size, Buffer, Size); } Storage->Size = NewSize; free (Storage->Buffer); Storage->Buffer = NewBuffer; } else { if ((Attributes & EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS) != 0) { CopyMem (&Storage->TimeStamp, TimeStamp, sizeof (*TimeStamp)); } Storage->Size = Size; ASSERT (Storage->Buffer != NULL); free (Storage->Buffer); Storage->Buffer = malloc (Size); if (Storage->Buffer == NULL) { Error (NULL, 0, 0, "Unable to allocate memory", NULL); return EFI_OUT_OF_RESOURCES; } CopyMem (Storage->Buffer, Buffer, Size); } return EFI_SUCCESS; } EFI_STATUS CreateVariableList ( IN LIST_ENTRY *StorageListHead, IN CHAR16 *Name, IN EFI_GUID *Guid, IN UINT32 Attributes, IN EFI_TIME *TimeStamp, IN UINTN Size, IN UINT8 *Buffer, IN BOOLEAN Append ) { VARIABLE_INFO_PRIVATE *Storage; UINTN VarNameSize; LIST_ENTRY *Link; EFI_TIME TempTimeStamp; if (((Attributes & EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS) != 0) && (TimeStamp == NULL)) { // // Fetch the current time based on UTC timezone // GetCurrentTime (&TempTimeStamp); TimeStamp = &TempTimeStamp; } // // Check previous one // Link = FindVariableList (StorageListHead, Name, Guid); if (Link != NULL) { return UpdateVariableList ( Link, Name, Guid, Attributes, TimeStamp, Size, Buffer, Append ); } // // Create new one // Storage = malloc (sizeof (*Storage)); if (Storage == NULL) { Error (NULL, 0, 0, "Unable to allocate memory", NULL); return EFI_OUT_OF_RESOURCES; } Storage->Signature = VARIABLE_INFO_PRIVATE_SIGNATURE; CopyMem (&Storage->Guid, Guid, sizeof(*Guid)); VarNameSize = KeyEnrollStrSize (Name); Storage->Name = malloc (VarNameSize); if (Storage->Name == NULL) { Error (NULL, 0, 0, "Unable to allocate memory", NULL); free (Storage); return EFI_OUT_OF_RESOURCES; } CopyMem (Storage->Name, Name, VarNameSize); Storage->Attributes = Attributes; Storage->Size = Size; ASSERT (Storage->Size != 0); Storage->Buffer = malloc (Size); if (Storage->Buffer == NULL) { Error (NULL, 0, 0, "Unable to allocate memory", NULL); free (Storage->Name); free (Storage); return EFI_OUT_OF_RESOURCES; } CopyMem (Storage->Buffer, Buffer, Size); if ((Attributes & EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS) != 0) { CopyMem (&Storage->TimeStamp, TimeStamp, sizeof (*TimeStamp)); } InsertTailList(StorageListHead, &Storage->Link); return EFI_SUCCESS; } EFI_STATUS DeleteVariableList ( IN LIST_ENTRY *StorageListHead, IN CHAR16 *Name, IN EFI_GUID *Guid ) { VARIABLE_INFO_PRIVATE *Storage; LIST_ENTRY *Link; Link = FindVariableList (StorageListHead, Name, Guid); if (Link == NULL) { return EFI_NOT_FOUND; } Storage = VARIABLE_INFO_PRIVATE_FROM_LINK (Link); RemoveEntryList (&Storage->Link); DestroyStorage (Storage); return EFI_SUCCESS; } VOID SwapEndian ( IN UINT8 *Area, IN UINT32 Size ) /*++ Routine Description: Swap area from big endian to little endian Arguments: Area - Buffer address Size - Buffer size Returns: None --*/ { UINT8 Temp; UINTN Index; for (Index = 0; Index < (UINTN)Size / 2; Index++) { Temp = *(Area + Index); *(Area + Index) = *(Area + Size - 1 - Index); *(Area + Size - 1 - Index) = Temp; } } /** Load PE/COFF image information into internal buffer and check its validity. @retval EFI_SUCCESS Successful @retval EFI_UNSUPPORTED Invalid PE/COFF file @retval EFI_ABORTED Serious error occurs, like file I/O error etc. **/ EFI_STATUS LoadPeImage ( IN UINT8 *ImageBase, IN UINTN ImageSize, OUT EFI_IMAGE_SECURITY_DATA_DIRECTORY **SecDataDir ) { EFI_IMAGE_DOS_HEADER *DosHdr; EFI_IMAGE_NT_HEADERS32 *NtHeader32; EFI_IMAGE_NT_HEADERS64 *NtHeader64; EFI_IMAGE_OPTIONAL_HEADER_PTR_UNION NtHeader; UINT32 PeCoffHeaderOffset; UINT16 Magic; NtHeader32 = NULL; NtHeader64 = NULL; // // Read the Dos header // DosHdr = (EFI_IMAGE_DOS_HEADER *)(ImageBase); if (DosHdr->e_magic == EFI_IMAGE_DOS_SIGNATURE) { // // DOS image header is present, // So read the PE header after the DOS image header // PeCoffHeaderOffset = DosHdr->e_lfanew; } else { PeCoffHeaderOffset = 0; } // // Read PE header and check the signature validity and machine compatibility // NtHeader32 = (EFI_IMAGE_NT_HEADERS32 *) (ImageBase + PeCoffHeaderOffset); if (NtHeader32->Signature != EFI_IMAGE_NT_SIGNATURE) { return EFI_UNSUPPORTED; } NtHeader.Pe32 = NtHeader32; // // Measuring PE/COFF Image Header; // But CheckSum field and SECURITY data directory (certificate) are excluded // // // Get the magic value from the PE/COFF Optional Header // Magic = NtHeader.Pe32->OptionalHeader.Magic; // // Check the architecture field of PE header and get the Certificate Data Directory data // Note the size of FileHeader field is constant for both IA32 and X64 arch // if (Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) { *SecDataDir = (EFI_IMAGE_SECURITY_DATA_DIRECTORY *) &(NtHeader32->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY]); } else if (Magic == EFI_IMAGE_NT_OPTIONAL_HDR64_MAGIC) { NtHeader64 = (EFI_IMAGE_NT_HEADERS64 *) (ImageBase + PeCoffHeaderOffset); *SecDataDir = (EFI_IMAGE_SECURITY_DATA_DIRECTORY *) &(NtHeader64->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY]); } else { return EFI_UNSUPPORTED; } return EFI_SUCCESS; } /** Calculate hash of Pe/Coff image based on the authenticode image hashing in PE/COFF Specification 8.0 Appendix A @param[in] HashAlg Hash algorithm type. @retval TRUE Successfully hash image. @retval FALSE Fail in hash image. **/ BOOLEAN HashPeImage ( IN UINT32 HashAlg, IN UINT8 *ImageBase, IN UINTN ImageSize, OUT UINT8 *ImageDigest, OUT UINTN *ImageDigestSize, OUT EFI_GUID *CertType ) { BOOLEAN Status; EFI_IMAGE_DOS_HEADER *DosHdr; UINT32 PeCoffHeaderOffset; EFI_IMAGE_SECTION_HEADER *Section; UINT8 *HashBase; UINTN HashSize; UINTN SumOfBytesHashed; EFI_IMAGE_SECTION_HEADER *SectionHeader; UINTN Index; UINTN Pos; UINT16 Magic; EFI_IMAGE_OPTIONAL_HEADER_PTR_UNION Hdr; UINT32 NumberOfRvaAndSizes; UINT32 CertSize; EVP_MD_CTX ctx; const EVP_MD *md; SectionHeader = NULL; Status = FALSE; // // Initialize context of hash. // if (HashAlg == HASHALG_SHA1) { *ImageDigestSize = SHA1_DIGEST_SIZE; CopyMem (CertType, &gEfiCertSha1Guid, sizeof(*CertType)); } else if (HashAlg == HASHALG_SHA256) { *ImageDigestSize = SHA256_DIGEST_SIZE; CopyMem (CertType, &gEfiCertSha256Guid, sizeof(*CertType)); } else if (HashAlg == HASHALG_SHA384) { *ImageDigestSize = SHA384_DIGEST_SIZE; CopyMem (CertType, &gEfiCertSha384Guid, sizeof(*CertType)); } else if (HashAlg == HASHALG_SHA512) { *ImageDigestSize = SHA512_DIGEST_SIZE; CopyMem (CertType, &gEfiCertSha512Guid, sizeof(*CertType)); } else { printf ("HashPeImage - HashAlg : 0x%x\n", HashAlg); return FALSE; } ZeroMem (ImageDigest, *ImageDigestSize); switch (HashAlg) { case HASHALG_SHA1: md = EVP_sha1(); break; case HASHALG_SHA256: md = EVP_sha256(); break; case HASHALG_SHA384: md = EVP_sha384(); break; case HASHALG_SHA512: md = EVP_sha512(); break; default: md = NULL; break; } // // Check PE/COFF image // DosHdr = (EFI_IMAGE_DOS_HEADER *) (UINTN) ImageBase; PeCoffHeaderOffset = 0; if (DosHdr->e_magic == EFI_IMAGE_DOS_SIGNATURE) { PeCoffHeaderOffset = DosHdr->e_lfanew; } Hdr.Pe32 = (EFI_IMAGE_NT_HEADERS32 *)((UINT8 *) (UINTN) ImageBase + PeCoffHeaderOffset); if (Hdr.Pe32->Signature != EFI_IMAGE_NT_SIGNATURE) { printf ("Hdr.Pe32->Signature != EFI_IMAGE_NT_SIGNATURE\n"); Status = FALSE; goto Done; } // // PE/COFF Image Measurement // // NOTE: The following codes/steps are based upon the authenticode image hashing in // PE/COFF Specification 8.0 Appendix A. // // // 1. Load the image header into memory. // 2. Initialize a SHA hash context. EVP_DigestInit(&ctx, md); // // Measuring PE/COFF Image Header; // But CheckSum field and SECURITY data directory (certificate) are excluded // // // Get the magic value from the PE/COFF Optional Header // Magic = Hdr.Pe32->OptionalHeader.Magic; // // 3. Calculate the distance from the base of the image header to the image checksum address. // 4. Hash the image header from its base to beginning of the image checksum. // HashBase = ImageBase; if (Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) { // // Use PE32 offset. // NumberOfRvaAndSizes = Hdr.Pe32->OptionalHeader.NumberOfRvaAndSizes; HashSize = (UINTN) ((UINT8 *) (&Hdr.Pe32->OptionalHeader.CheckSum) - HashBase); } else { // // Use PE32+ offset. // NumberOfRvaAndSizes = Hdr.Pe32Plus->OptionalHeader.NumberOfRvaAndSizes; HashSize = (UINTN) ((UINT8 *) (&Hdr.Pe32Plus->OptionalHeader.CheckSum) - HashBase); } EVP_DigestUpdate(&ctx, HashBase, HashSize); // // 5. Skip over the image checksum (it occupies a single ULONG). // if (NumberOfRvaAndSizes <= EFI_IMAGE_DIRECTORY_ENTRY_SECURITY) { // // 6. Since there is no Cert Directory in optional header, hash everything // from the end of the checksum to the end of image header. // if (Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) { // // Use PE32 offset. // HashBase = (UINT8 *) &Hdr.Pe32->OptionalHeader.CheckSum + sizeof (UINT32); HashSize = Hdr.Pe32->OptionalHeader.SizeOfHeaders - (UINTN) (HashBase - ImageBase); } else { // // Use PE32+ offset. // HashBase = (UINT8 *) &Hdr.Pe32Plus->OptionalHeader.CheckSum + sizeof (UINT32); HashSize = Hdr.Pe32Plus->OptionalHeader.SizeOfHeaders - (UINTN) (HashBase - ImageBase); } if (HashSize != 0) { EVP_DigestUpdate(&ctx, HashBase, HashSize); } } else { // // 7. Hash everything from the end of the checksum to the start of the Cert Directory. // if (Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) { // // Use PE32 offset. // HashBase = (UINT8 *) &Hdr.Pe32->OptionalHeader.CheckSum + sizeof (UINT32); HashSize = (UINTN) ((UINT8 *) (&Hdr.Pe32->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY]) - HashBase); } else { // // Use PE32+ offset. // HashBase = (UINT8 *) &Hdr.Pe32Plus->OptionalHeader.CheckSum + sizeof (UINT32); HashSize = (UINTN) ((UINT8 *) (&Hdr.Pe32Plus->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY]) - HashBase); } if (HashSize != 0) { EVP_DigestUpdate(&ctx, HashBase, HashSize); } // // 8. Skip over the Cert Directory. (It is sizeof(IMAGE_DATA_DIRECTORY) bytes.) // 9. Hash everything from the end of the Cert Directory to the end of image header. // if (Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) { // // Use PE32 offset // HashBase = (UINT8 *) &Hdr.Pe32->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY + 1]; HashSize = Hdr.Pe32->OptionalHeader.SizeOfHeaders - (UINTN) ((UINT8 *) (&Hdr.Pe32->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY + 1]) - ImageBase); } else { // // Use PE32+ offset. // HashBase = (UINT8 *) &Hdr.Pe32Plus->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY + 1]; HashSize = Hdr.Pe32Plus->OptionalHeader.SizeOfHeaders - (UINTN) ((UINT8 *) (&Hdr.Pe32Plus->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY + 1]) - ImageBase); } EVP_DigestUpdate(&ctx, HashBase, HashSize); } // // 10. Set the SUM_OF_BYTES_HASHED to the size of the header. // if (Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) { // // Use PE32 offset. // SumOfBytesHashed = Hdr.Pe32->OptionalHeader.SizeOfHeaders; } else { // // Use PE32+ offset // SumOfBytesHashed = Hdr.Pe32Plus->OptionalHeader.SizeOfHeaders; } // // 11. Build a temporary table of pointers to all the IMAGE_SECTION_HEADER // structures in the image. The 'NumberOfSections' field of the image // header indicates how big the table should be. Do not include any // IMAGE_SECTION_HEADERs in the table whose 'SizeOfRawData' field is zero. // SectionHeader = (EFI_IMAGE_SECTION_HEADER *) malloc (sizeof (EFI_IMAGE_SECTION_HEADER) * Hdr.Pe32->FileHeader.NumberOfSections); if (SectionHeader == NULL) { printf ("SectionHeader == NULL\n"); Status = FALSE; goto Done; } ZeroMem (SectionHeader, sizeof (EFI_IMAGE_SECTION_HEADER) * Hdr.Pe32->FileHeader.NumberOfSections); // // 12. Using the 'PointerToRawData' in the referenced section headers as // a key, arrange the elements in the table in ascending order. In other // words, sort the section headers according to the disk-file offset of // the section. // Section = (EFI_IMAGE_SECTION_HEADER *) ( ImageBase + PeCoffHeaderOffset + sizeof (UINT32) + sizeof (EFI_IMAGE_FILE_HEADER) + Hdr.Pe32->FileHeader.SizeOfOptionalHeader ); for (Index = 0; Index < Hdr.Pe32->FileHeader.NumberOfSections; Index++) { Pos = Index; while ((Pos > 0) && (Section->PointerToRawData < SectionHeader[Pos - 1].PointerToRawData)) { CopyMem (&SectionHeader[Pos], &SectionHeader[Pos - 1], sizeof (EFI_IMAGE_SECTION_HEADER)); Pos--; } CopyMem (&SectionHeader[Pos], Section, sizeof (EFI_IMAGE_SECTION_HEADER)); Section += 1; } // // 13. Walk through the sorted table, bring the corresponding section // into memory, and hash the entire section (using the 'SizeOfRawData' // field in the section header to determine the amount of data to hash). // 14. Add the section's 'SizeOfRawData' to SUM_OF_BYTES_HASHED . // 15. Repeat steps 13 and 14 for all the sections in the sorted table. // for (Index = 0; Index < Hdr.Pe32->FileHeader.NumberOfSections; Index++) { Section = &SectionHeader[Index]; if (Section->SizeOfRawData == 0) { continue; } HashBase = ImageBase + Section->PointerToRawData; HashSize = (UINTN) Section->SizeOfRawData; EVP_DigestUpdate(&ctx, HashBase, HashSize); SumOfBytesHashed += HashSize; } // // 16. If the file size is greater than SUM_OF_BYTES_HASHED, there is extra // data in the file that needs to be added to the hash. This data begins // at file offset SUM_OF_BYTES_HASHED and its length is: // FileSize - (CertDirectory->Size) // if (ImageSize > SumOfBytesHashed) { HashBase = ImageBase + SumOfBytesHashed; if (NumberOfRvaAndSizes <= EFI_IMAGE_DIRECTORY_ENTRY_SECURITY) { CertSize = 0; } else { if (Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) { // // Use PE32 offset. // CertSize = Hdr.Pe32->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY].Size; } else { // // Use PE32+ offset. // CertSize = Hdr.Pe32Plus->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY].Size; } } if (ImageSize > CertSize + SumOfBytesHashed) { HashSize = (UINTN) (ImageSize - CertSize - SumOfBytesHashed); EVP_DigestUpdate(&ctx, HashBase, HashSize); } } EVP_DigestFinal(&ctx, (UINT8 *)ImageDigest, NULL); Status = TRUE; Done: if (SectionHeader != NULL) { free (SectionHeader); } return Status; } /** Recognize the Hash algorithm in PE/COFF Authenticode and calculate hash of Pe/Coff image based on the authenticated image hashing in PE/COFF Specification 8.0 Appendix A @retval EFI_UNSUPPORTED Hash algorithm is not supported. @retval EFI_SUCCESS Hash successfully. **/ EFI_STATUS HashPeImageByType ( IN UINT8 *ImageBase, IN UINTN ImageSize, IN UINT8 *AuthData, IN UINTN AuthDataSize, OUT UINT8 *ImageDigest, OUT UINTN *ImageDigestSize, OUT EFI_GUID *CertType ) { UINT8 Index; for (Index = 0; Index < HASHALG_MAX; Index++) { // // Check the Hash algorithm in PE/COFF Authenticode. // According to PKCS#7 Definition: // SignedData ::= SEQUENCE { // version Version, // digestAlgorithms DigestAlgorithmIdentifiers, // contentInfo ContentInfo, // .... } // The DigestAlgorithmIdentifiers can be used to determine the hash algorithm in PE/COFF hashing // This field has the fixed offset (+32) in final Authenticode ASN.1 data. // Fixed offset (+32) is calculated based on two bytes of length encoding. // if ((*(AuthData + 1) & TWO_BYTE_ENCODE) != TWO_BYTE_ENCODE) { // // Only support two bytes of Long Form of Length Encoding. // continue; } if (AuthDataSize < 32 + mHash[Index].OidLength) { return EFI_UNSUPPORTED; } if (CompareMem (AuthData + 32, mHash[Index].OidValue, mHash[Index].OidLength) == 0) { break; } } if (Index == HASHALG_MAX) { return EFI_UNSUPPORTED; } // // HASH PE Image based on Hash algorithm in PE/COFF Authenticode. // if (!HashPeImage(Index, ImageBase, ImageSize, ImageDigest, ImageDigestSize, CertType)) { return EFI_UNSUPPORTED; } return EFI_SUCCESS; } EFI_STATUS CreateImageCertificateList ( IN CHAR8 *ImageFile, IN EFI_GUID *SignatureOwner, OUT EFI_SIGNATURE_LIST **Cert ) { STATUS Status; EFI_STATUS EfiStatus; UINT8 *ImageBase = NULL; UINTN ImageSize = 0; EFI_SIGNATURE_DATA *CertData=NULL; EFI_IMAGE_SECURITY_DATA_DIRECTORY *SecDataDir; WIN_CERTIFICATE *Certificate; UINT8 ImageDigest[MAX_DIGEST_SIZE]; UINTN ImageDigestSize; EFI_GUID CertType; WIN_CERTIFICATE_UEFI_GUID *GuidCertData; WIN_CERTIFICATE_EFI_PKCS *PkcsCertData; Status = ReadInputFile (ImageFile, &ImageBase, (UINT32 *) &ImageSize, NULL); if (Status != STATUS_SUCCESS) { Error (NULL, 0, 0, "Unable to open file", ImageFile); return EFI_NOT_FOUND; } EfiStatus = LoadPeImage (ImageBase, ImageSize, &SecDataDir); if (EFI_ERROR (EfiStatus)) { Error (NULL, 0, 0, "Unable to load PE image", ImageFile); return EFI_UNSUPPORTED; } if (SecDataDir->SizeOfCert == 0) { if (!HashPeImage (HASHALG_SHA256, ImageBase, ImageSize, ImageDigest, &ImageDigestSize, &CertType)) { Error (NULL, 0, 0, "Unable to hash PE image", ImageFile); free (ImageBase); return EFI_UNSUPPORTED; } } else { // // Read the certificate data // Certificate = (WIN_CERTIFICATE *)(ImageBase + SecDataDir->Offset); if (Certificate->wCertificateType == WIN_CERT_TYPE_EFI_GUID) { GuidCertData = (WIN_CERTIFICATE_UEFI_GUID *) Certificate; if (CompareMem (&GuidCertData->CertType, &gEfiCertTypeRsa2048Sha256Guid, sizeof(EFI_GUID)) == 0) { if (!HashPeImage (HASHALG_SHA256, ImageBase, ImageSize, ImageDigest, &ImageDigestSize, &CertType)) { Error (NULL, 0, 0, "Image wCertificateType is EFI_GUID, but unable to hash PE image", ImageFile); free (ImageBase); return EFI_UNSUPPORTED; } } else if (CompareMem (&GuidCertData->CertType, &gEfiCertTypePkcs7Guid, sizeof(EFI_GUID)) == 0) { Status = HashPeImageByType ( ImageBase, ImageSize, (UINT8 *)GuidCertData + sizeof(GuidCertData->Hdr) + sizeof(GuidCertData->CertType), GuidCertData->Hdr.dwLength - sizeof(GuidCertData->Hdr) - sizeof(GuidCertData->CertType), ImageDigest, &ImageDigestSize, &CertType ); if (EFI_ERROR (Status)) { Error (NULL, 0, 0, "Image wCertificateType is EFI_GUID, Unable to hash PE image by type", ImageFile); free (ImageBase); return EFI_UNSUPPORTED; } } else { Error (NULL, 0, 0, "Image wCertificateType is EFI_GUID, but CertType is NOT gEfiCertTypeRsa2048Sha256Guid or gEfiCertTypePkcs7Guid", ImageFile); free (ImageBase); return EFI_UNSUPPORTED; } } else if (Certificate->wCertificateType == WIN_CERT_TYPE_PKCS_SIGNED_DATA) { PkcsCertData = (WIN_CERTIFICATE_EFI_PKCS *) Certificate; Status = HashPeImageByType ( ImageBase, ImageSize, PkcsCertData->CertData, PkcsCertData->Hdr.dwLength - sizeof(PkcsCertData->Hdr), ImageDigest, &ImageDigestSize, &CertType ); if (EFI_ERROR (Status)) { Error (NULL, 0, 0, "Image wCertificateType is PKCS_SIGNED_DATA, Unable to hash PE image by type", ImageFile); free (ImageBase); return EFI_UNSUPPORTED; } } else { Error (NULL, 0, 0, "Image wCertificateType is unknown", ImageFile); free (ImageBase); return EFI_UNSUPPORTED; } } // Allocate space for PK certificate list and initialize it. // Create PK database entry w/ SignatureHeaderSize equals 0. // *Cert = (EFI_SIGNATURE_LIST*) malloc ( sizeof(EFI_SIGNATURE_LIST) + sizeof(EFI_SIGNATURE_DATA) - 1 + ImageDigestSize ); if (*Cert == NULL) { Error (NULL, 0, 0, "Unable to allocate memory", NULL); free (ImageBase); return EFI_OUT_OF_RESOURCES; } (*Cert)->SignatureListSize = (UINT32) (sizeof(EFI_SIGNATURE_LIST) + sizeof(EFI_SIGNATURE_DATA) - 1 + ImageDigestSize); (*Cert)->SignatureSize = (UINT32) (sizeof(EFI_SIGNATURE_DATA) - 1 + ImageDigestSize); (*Cert)->SignatureHeaderSize = 0; CopyMem (&(*Cert)->SignatureType, &CertType, sizeof(EFI_GUID)); CertData = (EFI_SIGNATURE_DATA*) ((UINTN)(*Cert) + sizeof(EFI_SIGNATURE_LIST) + (*Cert)->SignatureHeaderSize); CopyMem (&CertData->SignatureOwner, SignatureOwner, sizeof(EFI_GUID)); CopyMem ((VOID*)&(CertData->SignatureData[0]), ImageDigest, ImageDigestSize); free (ImageBase); return EFI_SUCCESS; } EFI_STATUS CreatePkCertificateList ( IN CHAR8 *PkKeyFile, IN EFI_GUID *SignatureOwner, OUT EFI_SIGNATURE_LIST **Cert ) { STATUS Status; UINT8 *PkKeyData = NULL; UINTN PkKeyDataSize = 0; EFI_SIGNATURE_DATA *CertData=NULL; CPL_KEY_INFO *KeyInfo; VOID *KeyBuffer = NULL; Status = ReadInputFile (PkKeyFile, &PkKeyData, (UINT32 *) &PkKeyDataSize, NULL); if (Status != STATUS_SUCCESS) { Error (NULL, 0, 0, "Unable to open file", PkKeyFile); return EFI_NOT_FOUND; } KeyInfo = (CPL_KEY_INFO *)PkKeyData; if (KeyInfo->KeyLengthInBits/8 != WIN_CERT_UEFI_RSA2048_SIZE) { Error (NULL, 0, 0, "PkKey data file format wrong", PkKeyFile); free (PkKeyData); return EFI_UNSUPPORTED; } KeyBuffer = (VOID *)(KeyInfo + 1); SwapEndian (KeyBuffer, WIN_CERT_UEFI_RSA2048_SIZE); // Allocate space for PK certificate list // and initialize the list. // Create PK database entry w/ SignatureHeaderSize equals 0. // *Cert = (EFI_SIGNATURE_LIST*) malloc ( sizeof(EFI_SIGNATURE_LIST) + sizeof(EFI_SIGNATURE_DATA) - 1 + WIN_CERT_UEFI_RSA2048_SIZE ); if (*Cert == NULL) { Error (NULL, 0, 0, "Unable to allocate memory", NULL); free (PkKeyData); return EFI_OUT_OF_RESOURCES; } (*Cert)->SignatureListSize = sizeof(EFI_SIGNATURE_LIST) + sizeof(EFI_SIGNATURE_DATA) - 1 + WIN_CERT_UEFI_RSA2048_SIZE; (*Cert)->SignatureSize = sizeof(EFI_SIGNATURE_DATA) - 1 + WIN_CERT_UEFI_RSA2048_SIZE; (*Cert)->SignatureHeaderSize = 0; CopyMem (&(*Cert)->SignatureType, &gEfiCertRsa2048Guid, sizeof(EFI_GUID)); CertData = (EFI_SIGNATURE_DATA*)((UINTN)(*Cert) + sizeof(EFI_SIGNATURE_LIST) + (*Cert)->SignatureHeaderSize); CopyMem (&CertData->SignatureOwner, SignatureOwner, sizeof(EFI_GUID)); // // Fill the PK database with PKpub data from PKKeyFile. // CopyMem ((VOID*)&(CertData->SignatureData[0]), KeyBuffer, WIN_CERT_UEFI_RSA2048_SIZE); free (PkKeyData); return EFI_SUCCESS; } BOOLEAN IsX509FileDER ( IN CONST UINT8 *X509Data, IN UINTN X509DataSize ) { X509 *X509Cert; // // Read DER-encoded X509 Certificate and Construct X509 object. // X509Cert = d2i_X509 (NULL, &X509Data, (long) X509DataSize); if (X509Cert == NULL) { return FALSE; } return TRUE; } BOOLEAN IsX509FilePEM ( IN CONST UINT8 *X509Data, IN UINTN X509DataSize ) { if (X509DataSize <= sizeof("-----BEGIN")) { return FALSE; } if (memcmp (X509Data, "-----BEGIN", sizeof("-----BEGIN") - 1) != 0) { return FALSE; } return TRUE; } EFI_STATUS CreatePkX509CertificateList ( IN CHAR8 *X509File, IN EFI_GUID *SignatureOwner, OUT EFI_SIGNATURE_LIST **Cert ) { STATUS Status; UINT8 *X509Data = NULL; UINTN X509DataSize = 0; EFI_SIGNATURE_DATA *CertData=NULL; Status = ReadInputFile (X509File, &X509Data, (UINT32 *) &X509DataSize, NULL); if (Status != STATUS_SUCCESS) { Error (NULL, 0, 0, "Unable to open file", X509File); return EFI_NOT_FOUND; } if (!IsX509FileDER (X509Data, X509DataSize)) { if (IsX509FilePEM (X509Data, X509DataSize)) { Error (NULL, 0, 0, "PEM format X509 file unsupported", X509File); } else { Error (NULL, 0, 0, "Non-DER format X509 file unsupported", X509File); } return EFI_UNSUPPORTED; } // Allocate space for PK certificate list and initialize it. // Create PK database entry w/ SignatureHeaderSize equals 0. // *Cert = (EFI_SIGNATURE_LIST*) malloc ( sizeof(EFI_SIGNATURE_LIST) + sizeof(EFI_SIGNATURE_DATA) - 1 + X509DataSize ); if (*Cert == NULL) { Error (NULL, 0, 0, "Unable to allocate memory", NULL); return EFI_OUT_OF_RESOURCES; } (*Cert)->SignatureListSize = (UINT32) (sizeof(EFI_SIGNATURE_LIST) + sizeof(EFI_SIGNATURE_DATA) - 1 + X509DataSize); (*Cert)->SignatureSize = (UINT32) (sizeof(EFI_SIGNATURE_DATA) - 1 + X509DataSize); (*Cert)->SignatureHeaderSize = 0; CopyMem (&(*Cert)->SignatureType, &gEfiCertX509Guid, sizeof(EFI_GUID)); CertData = (EFI_SIGNATURE_DATA*) ((UINTN)(*Cert) + sizeof(EFI_SIGNATURE_LIST) + (*Cert)->SignatureHeaderSize); CopyMem (&CertData->SignatureOwner, SignatureOwner, sizeof(EFI_GUID)); CopyMem ((VOID*)&(CertData->SignatureData[0]), X509Data, X509DataSize); free (X509Data); return EFI_SUCCESS; } EFI_STATUS CreatePkX509CertificateListHash ( IN CHAR8 *X509File, IN EFI_GUID *SignatureOwner, IN UINT32 HashAlg, IN EFI_TIME *Time, OUT EFI_SIGNATURE_LIST **Cert ) { STATUS Status; UINT8 *X509Data = NULL; UINTN X509DataSize = 0; EFI_SIGNATURE_DATA *CertData=NULL; UINT8 *TBSCert; UINTN TBSCertSize; EFI_CERT_X509_SHA256 X509Sha256; EFI_CERT_X509_SHA384 X509Sha384; EFI_CERT_X509_SHA512 X509Sha512; UINT8 *X509Hash; UINTN X509HashSize; EVP_MD_CTX ctx; const EVP_MD *md; Status = ReadInputFile (X509File, &X509Data, (UINT32 *) &X509DataSize, NULL); if (Status != STATUS_SUCCESS) { Error (NULL, 0, 0, "Unable to open file", X509File); return EFI_NOT_FOUND; } if (!IsX509FileDER (X509Data, X509DataSize)) { if (IsX509FilePEM (X509Data, X509DataSize)) { Error (NULL, 0, 0, "PEM format X509 file unsupported", X509File); } else { Error (NULL, 0, 0, "Non-DER format X509 file unsupported", X509File); } return EFI_UNSUPPORTED; } // // Retrieve the TBSCertificate from the X.509 Certificate. // if (!X509GetTBSCert (X509Data, X509DataSize, &TBSCert, &TBSCertSize)) { return EFI_UNSUPPORTED; } switch (HashAlg) { case HASHALG_SHA256: md = EVP_sha256(); EVP_DigestInit(&ctx, md); EVP_DigestUpdate(&ctx, TBSCert, TBSCertSize); EVP_DigestFinal(&ctx, (UINT8 *)&X509Sha256.ToBeSignedHash, NULL); CopyMem (&X509Sha256.TimeOfRevocation, Time, sizeof(EFI_TIME)); X509Hash = (UINT8 *)&X509Sha256; X509HashSize = sizeof(X509Sha256); break; case HASHALG_SHA384: md = EVP_sha384(); EVP_DigestInit(&ctx, md); EVP_DigestUpdate(&ctx, TBSCert, TBSCertSize); EVP_DigestFinal(&ctx, (UINT8 *)&X509Sha384.ToBeSignedHash, NULL); CopyMem (&X509Sha384.TimeOfRevocation, Time, sizeof(EFI_TIME)); X509Hash = (UINT8 *)&X509Sha384; X509HashSize = sizeof(X509Sha384); break; case HASHALG_SHA512: md = EVP_sha512(); EVP_DigestInit(&ctx, md); EVP_DigestUpdate(&ctx, TBSCert, TBSCertSize); EVP_DigestFinal(&ctx, (UINT8 *)&X509Sha512.ToBeSignedHash, NULL); CopyMem (&X509Sha512.TimeOfRevocation, Time, sizeof(EFI_TIME)); X509Hash = (UINT8 *)&X509Sha512; X509HashSize = sizeof(X509Sha512); break; default: assert (FALSE); md = NULL; X509Hash = NULL; X509HashSize = 0; break; } // Allocate space for PK certificate list and initialize it. // Create PK database entry w/ SignatureHeaderSize equals 0. // *Cert = (EFI_SIGNATURE_LIST*) malloc ( sizeof(EFI_SIGNATURE_LIST) + sizeof(EFI_SIGNATURE_DATA) - 1 + X509HashSize ); if (*Cert == NULL) { Error (NULL, 0, 0, "Unable to allocate memory", NULL); return EFI_OUT_OF_RESOURCES; } (*Cert)->SignatureListSize = (UINT32) (sizeof(EFI_SIGNATURE_LIST) + sizeof(EFI_SIGNATURE_DATA) - 1 + X509HashSize); (*Cert)->SignatureSize = (UINT32) (sizeof(EFI_SIGNATURE_DATA) - 1 + X509HashSize); (*Cert)->SignatureHeaderSize = 0; switch (HashAlg) { case HASHALG_SHA256: CopyMem (&(*Cert)->SignatureType, &gEfiCertX509Sha256Guid, sizeof(EFI_GUID)); break; case HASHALG_SHA384: CopyMem (&(*Cert)->SignatureType, &gEfiCertX509Sha384Guid, sizeof(EFI_GUID)); break; case HASHALG_SHA512: CopyMem (&(*Cert)->SignatureType, &gEfiCertX509Sha512Guid, sizeof(EFI_GUID)); break; } CertData = (EFI_SIGNATURE_DATA*) ((UINTN)(*Cert) + sizeof(EFI_SIGNATURE_LIST) + (*Cert)->SignatureHeaderSize); CopyMem (&CertData->SignatureOwner, SignatureOwner, sizeof(EFI_GUID)); CopyMem ((VOID*)&(CertData->SignatureData[0]), X509Hash, X509HashSize); free (X509Data); return EFI_SUCCESS; } EFI_STATUS EnrollPlatformKey ( IN CHAR8 *PkFile, IN EFI_GUID *SignatureOwner ) { EFI_STATUS EfiStatus; EFI_SIGNATURE_LIST *PkCert; EfiStatus = CreatePkX509CertificateList ( PkFile, SignatureOwner, &PkCert ); if (EFI_ERROR (EfiStatus)) { printf ("EnrollPlatformKey - CreatePkX509CertificateList fail!\n"); return EfiStatus; } EfiStatus = CreateVariableList ( &mVarListEntry, EFI_PLATFORM_KEY_NAME, &gEfiGlobalVariableGuid, EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS, NULL, PkCert->SignatureListSize, (UINT8 *)PkCert, FALSE ); if (!EFI_ERROR (EfiStatus)) { printf ("EnrollPlatformKey - success!\n"); } else { printf ("EnrollPlatformKey - fail!\n"); return EfiStatus; } return EfiStatus; } EFI_STATUS DeletePlatformKey ( VOID ) { EFI_STATUS EfiStatus; EfiStatus = DeleteVariableList ( &mVarListEntry, EFI_PLATFORM_KEY_NAME, &gEfiGlobalVariableGuid ); if (EFI_ERROR (EfiStatus)) { printf ("DeletePlatformKey - not found!\n"); return EfiStatus; } printf ("DeletePlatformKey - success!\n"); return EfiStatus; } EFI_STATUS EnrollKeyExchangeKey ( IN CHAR8 *PkFile, IN EFI_GUID *SignatureOwner, IN BOOLEAN Append, IN BOOLEAN IsCertFile ) { EFI_STATUS EfiStatus; EFI_SIGNATURE_LIST *KekCert; if (IsCertFile) { EfiStatus = CreatePkX509CertificateList ( PkFile, SignatureOwner, &KekCert ); if (EFI_ERROR (EfiStatus)) { printf ("EnrollKeyExchangeKey - CreatePkX509CertificateList fail!\n"); return EfiStatus; } } else { EfiStatus = CreatePkCertificateList ( PkFile, SignatureOwner, &KekCert ); if (EFI_ERROR (EfiStatus)) { printf ("EnrollKeyExchangeKey - CreatePkCertificateList fail!\n"); return EfiStatus; } } EfiStatus = CreateVariableList ( &mVarListEntry, EFI_KEY_EXCHANGE_KEY_NAME, &gEfiGlobalVariableGuid, EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS, NULL, KekCert->SignatureListSize, (UINT8 *)KekCert, Append ); if (!EFI_ERROR (EfiStatus)) { printf ("EnrollKeyExchangeKey - success!\n"); } else { printf ("EnrollKeyExchangeKey - fail!\n"); } return EFI_SUCCESS; } EFI_STATUS DeleteKeyExchangeKey ( VOID ) { EFI_STATUS EfiStatus; EfiStatus = DeleteVariableList ( &mVarListEntry, EFI_KEY_EXCHANGE_KEY_NAME, &gEfiGlobalVariableGuid ); if (EFI_ERROR (EfiStatus)) { printf ("DeleteKeyExchangeKey - not found!\n"); return EfiStatus; } printf ("DeleteKeyExchangeKey - success!\n"); return EfiStatus; } EFI_STATUS EnrollSignatureDBHash ( IN CHAR8 *PkFile, IN EFI_GUID *SignatureOwner, IN BOOLEAN Append, IN UINT8 DbIndex, IN UINT32 HashAlg, IN EFI_TIME *Time ) { EFI_STATUS EfiStatus; EFI_SIGNATURE_LIST *DbCert; CHAR16 *Name; EfiStatus = CreatePkX509CertificateListHash ( PkFile, SignatureOwner, HashAlg, Time, &DbCert ); if (EFI_ERROR (EfiStatus)) { printf ("EnrollSignatureDBHash - CreatePkX509CertificateListHash fail!\n"); return EfiStatus; } switch (DbIndex) { case 0: Name = EFI_IMAGE_SECURITY_DATABASE; break; case 1: Name = EFI_IMAGE_SECURITY_DATABASE1; break; case 2: Name = EFI_IMAGE_SECURITY_DATABASE2; break; default: Name = NULL; assert(FALSE); break; } ASSERT (Name != NULL); EfiStatus = CreateVariableList ( &mVarListEntry, Name, &gEfiImageSecurityDatabaseGuid, EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS, NULL, DbCert->SignatureListSize, (UINT8 *)DbCert, Append ); if (!EFI_ERROR (EfiStatus)) { printf ("EnrollSignatureDBHash - success!\n"); } else { printf ("EnrollSignatureDBHash - fail!\n"); } return EFI_SUCCESS; } EFI_STATUS EnrollSignatureDB ( IN CHAR8 *PkFile, IN EFI_GUID *SignatureOwner, IN BOOLEAN Append, IN UINT8 DbIndex, IN BOOLEAN IsCertFile ) { EFI_STATUS EfiStatus; EFI_SIGNATURE_LIST *DbCert; CHAR16 *Name; if (IsCertFile) { EfiStatus = CreatePkX509CertificateList ( PkFile, SignatureOwner, &DbCert ); if (EFI_ERROR (EfiStatus)) { printf ("EnrollSignatureDB - CreatePkX509CertificateList fail!\n"); return EfiStatus; } } else { EfiStatus = CreateImageCertificateList ( PkFile, SignatureOwner, &DbCert ); if (EFI_ERROR (EfiStatus)) { printf ("EnrollSignatureDB - CreateImageCertificateList fail!\n"); return EfiStatus; } } switch (DbIndex) { case 0: Name = EFI_IMAGE_SECURITY_DATABASE; break; case 1: Name = EFI_IMAGE_SECURITY_DATABASE1; break; case 2: Name = EFI_IMAGE_SECURITY_DATABASE2; break; default: Name = NULL; assert(FALSE); break; } ASSERT (Name != NULL); EfiStatus = CreateVariableList ( &mVarListEntry, Name, &gEfiImageSecurityDatabaseGuid, EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS, NULL, DbCert->SignatureListSize, (UINT8 *)DbCert, Append ); if (!EFI_ERROR (EfiStatus)) { printf ("EnrollSignatureDB - success!\n"); } else { printf ("EnrollSignatureDB - fail!\n"); } return EFI_SUCCESS; } EFI_STATUS DeleteSignatureDB ( IN UINT8 DbIndex ) { CHAR16 *Name; EFI_STATUS EfiStatus; switch (DbIndex) { case 0: Name = EFI_IMAGE_SECURITY_DATABASE; break; case 1: Name = EFI_IMAGE_SECURITY_DATABASE1; break; case 2: Name = EFI_IMAGE_SECURITY_DATABASE2; break; default: Name = NULL; assert(FALSE); break; } ASSERT (Name != NULL); EfiStatus = DeleteVariableList ( &mVarListEntry, Name, &gEfiImageSecurityDatabaseGuid ); if (EFI_ERROR (EfiStatus)) { printf ("DeleteSignatureDB - not found!\n"); return EfiStatus; } printf ("DeleteSignatureDB - success!\n"); return EfiStatus; } STATUS ProcessPk ( IN INTN argc, IN CHAR8 **argv ) { EFI_GUID Guid; if ((strcmp (argv[4], "ADD") == 0) || (strcmp (argv[4], "add") == 0)) { if ((argc != 7) && (argc != 9)) { Error (NULL, 0, 0, "PK ADD: invalid number of input parameters specified", NULL); return STATUS_ERROR; } if ((strcmp (argv[5], "-F") != 0) && (strcmp (argv[5], "-f") != 0)) { Error (NULL, 0, 0, "PK ADD: -F is not specified", NULL); return STATUS_ERROR; } if (argc == 9) { if ((strcmp (argv[7], "-G") != 0) && (strcmp (argv[7], "-g") != 0)) { Error (NULL, 0, 0, "PK ADD: -G is not specified", NULL); return STATUS_ERROR; } if (!IsGuidData (argv[8], &Guid)) { Error (NULL, 0, 0, "PK ADD: Unknown Guid - ", argv[8]); return STATUS_ERROR; } } else { CopyMem (&Guid, &gEfiGlobalVariableGuid, sizeof(Guid)); } return EnrollPlatformKey (argv[6], &Guid); } else if ((strcmp (argv[4], "DEL") == 0) || (strcmp (argv[4], "del") == 0)) { if (argc != 5) { Error (NULL, 0, 0, "PK DEL: invalid number of input parameters specified", NULL); return STATUS_ERROR; } return DeletePlatformKey (); } Error (NULL, 0, 0, "PK: Unknown SubCommand - ", argv[4]); return STATUS_ERROR; } STATUS ProcessKek ( IN INTN argc, IN CHAR8 **argv ) { EFI_GUID Guid; BOOLEAN Append; BOOLEAN IsCertFile; if ((strcmp (argv[4], "ADD") == 0) || (strcmp (argv[4], "add") == 0) || (strcmp (argv[4], "APPEND") == 0) || (strcmp (argv[4], "append") == 0)) { if (argc != 9) { Error (NULL, 0, 0, "KEK ADD|APPEND: invalid number of input parameters specified", NULL); return STATUS_ERROR; } if ((strcmp (argv[5], "-F") != 0) && (strcmp (argv[5], "-f") != 0) && (strcmp (argv[5], "-K") != 0) && (strcmp (argv[5], "-k") != 0)) { Error (NULL, 0, 0, "KEK ADD|APPEND: -F|-K is not specified", NULL); return STATUS_ERROR; } if ((strcmp (argv[7], "-G") != 0) && (strcmp (argv[7], "-g") != 0)) { Error (NULL, 0, 0, "KEK ADD|APPEND: -G is not specified", NULL); return STATUS_ERROR; } if (!IsGuidData (argv[8], &Guid)) { Error (NULL, 0, 0, "KEK ADD|APPEND: Unknown Guid - ", argv[8]); return STATUS_ERROR; } if ((strcmp (argv[4], "APPEND") == 0) || (strcmp (argv[4], "append") == 0)) { Append = TRUE; } else { Append = FALSE; } if ((strcmp (argv[5], "-F") == 0) || (strcmp (argv[5], "-f") == 0)) { IsCertFile = TRUE; } else { IsCertFile = FALSE; } return EnrollKeyExchangeKey (argv[6], &Guid, Append, IsCertFile); } else if ((strcmp (argv[4], "DEL") == 0) || (strcmp (argv[4], "del") == 0)) { if (argc != 5) { Error (NULL, 0, 0, "KEK DEL: invalid number of input parameters specified", NULL); return STATUS_ERROR; } return DeleteKeyExchangeKey (); } Error (NULL, 0, 0, "KEK: Unknown SubCommand - ", argv[4]); return STATUS_ERROR; } STATUS ProcessDb ( IN INTN argc, IN CHAR8 **argv, IN UINT8 DbIndex ) { EFI_GUID Guid; BOOLEAN Append; BOOLEAN IsCertFile; UINT32 HashAlg; EFI_TIME Time; if ((strcmp (argv[4], "ADD") == 0) || (strcmp (argv[4], "add") == 0) || (strcmp (argv[4], "APPEND") == 0) || (strcmp (argv[4], "append") == 0)) { if (argc < 9) { Error (NULL, 0, 0, "db|dbx|dbt ADD|APPEND: invalid number of input parameters specified", NULL); return STATUS_ERROR; } if ((strcmp (argv[5], "-F") != 0) && (strcmp (argv[5], "-f") != 0) && (strcmp (argv[5], "-I") != 0) && (strcmp (argv[5], "-i") != 0)) { Error (NULL, 0, 0, "db|dbx|dbt ADD|APPEND: -F|-I is not specified", NULL); return STATUS_ERROR; } if ((strcmp (argv[7], "-G") != 0) && (strcmp (argv[7], "-g") != 0)) { Error (NULL, 0, 0, "db|dbx|dbt ADD|APPEND: -G is not specified", NULL); return STATUS_ERROR; } if (!IsGuidData (argv[8], &Guid)) { Error (NULL, 0, 0, "db|dbx|dbt ADD|APPEND: Unknown Guid - ", argv[8]); return STATUS_ERROR; } if ((strcmp (argv[4], "APPEND") == 0) || (strcmp (argv[4], "append") == 0)) { Append = TRUE; } else { Append = FALSE; } if ((strcmp (argv[5], "-F") == 0) || (strcmp (argv[5], "-f") == 0)) { IsCertFile = TRUE; } else { IsCertFile = FALSE; } if (argc == 9) { return EnrollSignatureDB (argv[6], &Guid, Append, DbIndex, IsCertFile); } // // Hash // if (argc < 11) { Error (NULL, 0, 0, "db|dbx|dbt ADD|APPEND: invalid number of paramter for cert hash", NULL); } if ((strcmp (argv[9], "-H") != 0) && (strcmp (argv[9], "-h") != 0)) { Error (NULL, 0, 0, "db|dbx|dbt ADD|APPEND: -H is not specified", NULL); return STATUS_ERROR; } HashAlg = HASHALG_MAX; if ((strcmp (argv[10], "SHA256") == 0) || (strcmp (argv[10], "sha256") == 0) ) { HashAlg = HASHALG_SHA256; } else if ((strcmp (argv[10], "SHA384") == 0) || (strcmp (argv[10], "sha384") == 0) ) { HashAlg = HASHALG_SHA384; } else if ((strcmp (argv[10], "SHA512") == 0) || (strcmp (argv[10], "sha512") == 0) ) { HashAlg = HASHALG_SHA512; } if (HashAlg == HASHALG_MAX) { Error (NULL, 0, 0, "db|dbx|dbt ADD|APPEND: Unknown Hash Algorithm", NULL); return STATUS_ERROR; } ZeroMem (&Time, sizeof(EFI_TIME)); if (argc != 11) { if (argc != 15) { Error (NULL, 0, 0, "db|dbx|dbt ADD|APPEND: invalid number of paramter for cert time", NULL); return STATUS_ERROR; } if ((strcmp (argv[11], "-DATE") != 0) && (strcmp (argv[11], "-date") != 0)) { Error (NULL, 0, 0, "db|dbx|dbt ADD|APPEND: -DATE is not specified", NULL); return STATUS_ERROR; } if (!IsDateData (argv[12], &Time)) { Error (NULL, 0, 0, "db|dbx|dbt ADD|APPEND: Unknown Date - ", argv[12]); return STATUS_ERROR; } if ((strcmp (argv[13], "-TIME") != 0) && (strcmp (argv[13], "-time") != 0)) { Error (NULL, 0, 0, "db|dbx|dbt ADD|APPEND: -TIME is not specified", NULL); return STATUS_ERROR; } if (!IsTimeData (argv[14], &Time)) { Error (NULL, 0, 0, "db|dbx|dbt ADD|APPEND: Unknown Time - ", argv[14]); return STATUS_ERROR; } } return EnrollSignatureDBHash (argv[6], &Guid, Append, DbIndex, HashAlg, &Time); } else if ((strcmp (argv[4], "DEL") == 0) || (strcmp (argv[4], "del") == 0)) { if (argc != 5) { Error (NULL, 0, 0, "db|dbx|dbt DEL: invalid number of input parameters specified", NULL); return STATUS_ERROR; } return DeleteSignatureDB (DbIndex); } Error (NULL, 0, 0, "db|dbx|dbt: Unknown SubCommand - ", argv[4]); return STATUS_ERROR; } EFI_STATUS EnrollVar ( IN CHAR8 *Name, IN EFI_GUID *Guid, IN UINT32 Attributes, IN UINTN DataSize, IN VOID *Data, IN BOOLEAN TimeBased, IN BOOLEAN Append ) { EFI_STATUS EfiStatus; CHAR16 *UnicodeBuf; UINT8 *Payload; UINTN PayloadSize; EFI_TIME *TimeStamp; UnicodeBuf = malloc ((strlen(Name) + 1) * sizeof(CHAR16)); if (UnicodeBuf == NULL) { Error (NULL, 0, 0, "Unable to allocate memory for variable name", NULL); return EFI_OUT_OF_RESOURCES; } Ascii2UnicodeString (Name, UnicodeBuf); TimeStamp = NULL; Payload = (UINT8 *) Data; PayloadSize = DataSize; if (TimeBased) { TimeStamp = &((EFI_VARIABLE_AUTHENTICATION_2 *) Data)->TimeStamp; Payload = (UINT8 *) Data + AUTHINFO2_SIZE (Data); PayloadSize = DataSize - AUTHINFO2_SIZE (Data); } EfiStatus = CreateVariableList ( &mVarListEntry, UnicodeBuf, Guid, Attributes, TimeStamp, PayloadSize, Payload, Append ); if (!EFI_ERROR (EfiStatus)) { printf ("EnrollVar - success!\n"); } else { printf ("EnrollVar - fail!\n"); } free (UnicodeBuf); return EfiStatus; } EFI_STATUS DeleteVar ( IN CHAR8 *Name, IN EFI_GUID *Guid ) { EFI_STATUS EfiStatus; CHAR16 *UnicodeBuf; UnicodeBuf = malloc ((strlen(Name) + 1) * sizeof(CHAR16)); if (UnicodeBuf == NULL) { Error (NULL, 0, 0, "Unable to allocate memory", NULL); return EFI_OUT_OF_RESOURCES; } Ascii2UnicodeString (Name, UnicodeBuf); EfiStatus = DeleteVariableList ( &mVarListEntry, UnicodeBuf, Guid ); if (EFI_ERROR (EfiStatus)) { printf ("DeleteVar - not found!\n"); } else { printf ("DeleteVar - success!\n"); } free (UnicodeBuf); return EfiStatus; } /* This function will check and parse the command line parameter. @param[in] argc The number of input parameters. @param[in] argv Point to input each parameters. @param[out] SubCmd Point to the operation type. @retval EFI_SUCCESS All input parameters are valid. @retval EFI_INVALID_PARAMETER The number of input parameters or input parameters are invalid. */ EFI_STATUS ParsingVariableParams ( IN INTN argc, IN CHAR8 **argv, OUT EFI_COMMAND_OPER_TYPE *SubCmd ) { CHAR8 *SubCmdPtr; UINTN Index; if (argc != 9 && argc != 13 && argc != 14) { Error (NULL, 0, 0, "VAR: invalid number of input parameters specified", NULL); return EFI_INVALID_PARAMETER; } if (argv == NULL || SubCmd == NULL) { return EFI_INVALID_PARAMETER; } SubCmdPtr = malloc (strlen (argv[4]) + 1); if (SubCmdPtr == NULL) { return EFI_OUT_OF_RESOURCES; } memset (SubCmdPtr, 0, strlen (argv[4]) + 1); memcpy (SubCmdPtr, argv[4], strlen (argv[4]) + 1); for (Index = 0; Index < strlen (argv[4]) + 1; Index = Index + 1) { // // Convert lower letter to upper one. // if ((SubCmdPtr[Index] >= 'a') && (SubCmdPtr[Index] <= 'z')) { SubCmdPtr[Index] -= 32; } } if (strcmp (SubCmdPtr, "ADD") == 0) { *SubCmd = VariableTypeAdd; } else if (strcmp (SubCmdPtr, "DEL") == 0) { *SubCmd = VariableTypeDel; } else if (strcmp (SubCmdPtr, "APPEND") == 0) { *SubCmd = VariableTypeAppend; } else { Error (NULL, 0, 0, "VAR: Unknown SubCommand - ", argv[4]); return EFI_INVALID_PARAMETER; } if ( (strcmp (argv[5], "-N") != 0) && (strcmp (argv[5], "-n") != 0) ) { Error (NULL, 0, 0, "VAR: -N is not specified", NULL); return EFI_INVALID_PARAMETER; } if ( (strcmp (argv[7], "-G") != 0) && (strcmp (argv[7], "-g") != 0) ) { Error (NULL, 0, 0, "VAR: -G is not specified", NULL); return EFI_INVALID_PARAMETER; } if (argc >= 13) { if ( (strcmp (argv[9], "-A") != 0) && (strcmp (argv[9], "-a") != 0) ) { Error (NULL, 0, 0, "VAR: -A is not specified", NULL); return EFI_INVALID_PARAMETER; } if ( (strcmp (argv[11], "-D") != 0) && (strcmp (argv[11], "-d") != 0) ) { Error (NULL, 0, 0, "VAR: -D is not specified", NULL); return EFI_INVALID_PARAMETER; } } if (argc == 14) { if ( (strcmp (argv[13], "-T") != 0) && (strcmp (argv[13], "-t") != 0) ) { Error (NULL, 0, 0, "VAR: -T is not specified", NULL); return EFI_INVALID_PARAMETER; } } return EFI_SUCCESS; } STATUS ProcessVar ( IN INTN argc, IN CHAR8 **argv ) { EFI_STATUS VerifyStatus; EFI_COMMAND_OPER_TYPE CommandType; EFI_GUID Guid; UINT8 *Data; UINTN DataSize; STATUS Status; UINT32 Attributes; BOOLEAN TimeBased; Attributes = 0; Data = NULL; DataSize = 0; VerifyStatus = ParsingVariableParams (argc, argv, &CommandType); if (EFI_ERROR (VerifyStatus)) { return VerifyStatus; } // // Make "certdb" variable as private variable. // if (strcmp(argv[6], "certdb") == 0) { Error (NULL, 0, 0, "Invalid variable name.", NULL); return STATUS_ERROR; } if (!IsGuidData (argv[8], &Guid)) { Error (NULL, 0, 0, "Unknown Guid - ", argv[8]); return STATUS_ERROR; } if (argc >= 13) { Attributes = xtoi (argv[10]); Status = ReadInputFile (argv[12], &Data, (UINT32 *) &DataSize, NULL); if (Status != STATUS_SUCCESS) { Error (NULL, 0, 0, "Can not get data - ", argv[12]); return STATUS_ERROR; } } TimeBased = (argc == 14) ? TRUE : FALSE; if ( ((Attributes & EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS) != 0) && !TimeBased ) { Error (NULL, 0, 0, "EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS set is not matching -T flag.", NULL); return STATUS_ERROR; } if ( ((Attributes & EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS) == 0) && TimeBased ) { Error (NULL, 0, 0, "EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS set is not matching -T flag.", NULL); return STATUS_ERROR; } // // Non-timebased or "db","dbx","dbt","PK","KEY" variable will use previous logic to handling. // if ( strcmp(argv[6], "db") == 0 || strcmp(argv[6], "dbx") == 0 || strcmp(argv[6], "dbt") == 0 || strcmp(argv[6], "PK") == 0 || strcmp(argv[6], "KEY") == 0 || !TimeBased ) { if (CommandType == VariableTypeDel) { Status = DeleteVar (argv[6], &Guid); } else { Status = EnrollVar ( argv[6], &Guid, Attributes, DataSize, Data, TimeBased, CommandType == VariableTypeAppend ? TRUE : FALSE ); } } else { Status = ProcessAuthVar ( argv[6], &Guid, Data, DataSize, Attributes, CommandType ); } FREE_NON_NULL_PTR (Data); return Status; } VOID DumpTimestamp ( IN EFI_TIME *TimeOfRevocation ) { printf (" %02d/%02d/%04d %02d:%02d:%02d\n", TimeOfRevocation->Month, TimeOfRevocation->Day, TimeOfRevocation->Year, TimeOfRevocation->Hour, TimeOfRevocation->Minute, TimeOfRevocation->Second ); } VOID DumpCert ( IN UINT8 *Buffer, IN UINTN Size ) { EFI_SIGNATURE_LIST *CertList; EFI_SIGNATURE_DATA *Cert; UINT32 CertCount; UINT32 CertListIndex; UINT32 CertIndex; CertListIndex = 0; CertList = (EFI_SIGNATURE_LIST *)Buffer; while ((UINTN)CertList < (UINTN)Buffer + Size) { printf ("SIGNATURE_LIST[%d]\n", CertListIndex); printf (" SignatureType - %08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x\n", CertList->SignatureType.Data1, CertList->SignatureType.Data2, CertList->SignatureType.Data3, CertList->SignatureType.Data4[0], CertList->SignatureType.Data4[1], CertList->SignatureType.Data4[2], CertList->SignatureType.Data4[3], CertList->SignatureType.Data4[4], CertList->SignatureType.Data4[5], CertList->SignatureType.Data4[6], CertList->SignatureType.Data4[7] ); printf (" SignatureListSize - %08x\n", CertList->SignatureListSize); printf (" SignatureHeaderSize - %08x\n", CertList->SignatureHeaderSize); printf (" SignatureSize - %08x\n", CertList->SignatureSize); Cert = (EFI_SIGNATURE_DATA *)((UINT8 *)CertList + sizeof(EFI_SIGNATURE_LIST) + CertList->SignatureHeaderSize); CertCount = (CertList->SignatureListSize - sizeof(EFI_SIGNATURE_LIST) - CertList->SignatureHeaderSize) / CertList->SignatureSize; for (CertIndex = 0; CertIndex < CertCount; CertIndex++) { printf (" SIGNATURE_DATA[%d]\n", CertIndex); printf (" SignatureOwner - %08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x\n", Cert->SignatureOwner.Data1, Cert->SignatureOwner.Data2, Cert->SignatureOwner.Data3, Cert->SignatureOwner.Data4[0], Cert->SignatureOwner.Data4[1], Cert->SignatureOwner.Data4[2], Cert->SignatureOwner.Data4[3], Cert->SignatureOwner.Data4[4], Cert->SignatureOwner.Data4[5], Cert->SignatureOwner.Data4[6], Cert->SignatureOwner.Data4[7] ); printf (" Signature - \n"); if (CompareGuid (&CertList->SignatureType, &gEfiCertX509Sha256Guid) == 0) { EFI_CERT_X509_SHA256 *CertX509Sha256; CertX509Sha256 = (EFI_CERT_X509_SHA256 *)Cert->SignatureData; printf (" SHA256 - \n"); DumpHex (CertX509Sha256->ToBeSignedHash, sizeof(CertX509Sha256->ToBeSignedHash)); printf (" Timestamp - \n"); DumpTimestamp (&CertX509Sha256->TimeOfRevocation); } else if (CompareGuid (&CertList->SignatureType, &gEfiCertX509Sha384Guid) == 0) { EFI_CERT_X509_SHA384 *CertX509Sha384; CertX509Sha384 = (EFI_CERT_X509_SHA384 *)Cert->SignatureData; printf (" SHA384 - \n"); DumpHex (CertX509Sha384->ToBeSignedHash, sizeof(CertX509Sha384->ToBeSignedHash)); printf (" Timestamp - \n"); DumpTimestamp (&CertX509Sha384->TimeOfRevocation); } else if (CompareGuid (&CertList->SignatureType, &gEfiCertX509Sha512Guid) == 0) { EFI_CERT_X509_SHA512 *CertX509Sha512; CertX509Sha512 = (EFI_CERT_X509_SHA512 *)Cert->SignatureData; printf (" SHA512 - \n"); DumpHex (CertX509Sha512->ToBeSignedHash, sizeof(CertX509Sha512->ToBeSignedHash)); printf (" Timestamp - \n"); DumpTimestamp (&CertX509Sha512->TimeOfRevocation); } else { DumpHex (Cert->SignatureData, CertList->SignatureSize - (sizeof(EFI_SIGNATURE_DATA) - 1)); } Cert = (EFI_SIGNATURE_DATA *)((UINT8 *)Cert + CertList->SignatureSize); } CertList = (EFI_SIGNATURE_LIST *)((UINT8 *)CertList + CertList->SignatureListSize); CertListIndex ++; } } VOID DumpVarStorage ( IN VARIABLE_INFO_PRIVATE *Storage, IN BOOLEAN IsDumpCert ) { printf ("Variable:\n"); printf (" Name - %S\n", (wchar_t*)Storage->Name); printf (" Guid - %08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x\n", Storage->Guid.Data1, Storage->Guid.Data2, Storage->Guid.Data3, Storage->Guid.Data4[0], Storage->Guid.Data4[1], Storage->Guid.Data4[2], Storage->Guid.Data4[3], Storage->Guid.Data4[4], Storage->Guid.Data4[5], Storage->Guid.Data4[6], Storage->Guid.Data4[7] ); printf (" Attributes - %08x\n", Storage->Attributes); printf (" Size - %08x\n", (UINT32) Storage->Size); printf (" Buffer - \n"); if (IsDumpCert) { DumpCert (Storage->Buffer, Storage->Size); } else { DumpHex (Storage->Buffer, Storage->Size); } printf ("\n"); } VOID DumpInfo ( IN CHAR16 *Name, IN EFI_GUID *Guid ) { LIST_ENTRY *Link; VARIABLE_INFO_PRIVATE *Storage; Link = FindVariableList ( &mVarListEntry, Name, Guid ); if (Link == NULL) { printf ("DumpInfo - not found!\n"); return ; } Storage = VARIABLE_INFO_PRIVATE_FROM_LINK (Link); DumpVarStorage (Storage, TRUE); return ; } VOID DumpVariableList ( IN LIST_ENTRY *StorageListHead ) { LIST_ENTRY *Link; VARIABLE_INFO_PRIVATE *Storage; if (StorageListHead->ForwardLink != NULL) { Link = GetFirstNode (StorageListHead); while (!IsNull (StorageListHead, Link)) { Storage = VARIABLE_INFO_PRIVATE_FROM_LINK (Link); DumpVarStorage (Storage, FALSE); Link = GetNextNode (StorageListHead, Link); } } } STATUS ProcessInfo ( IN INTN argc, IN CHAR8 **argv ) { if ((argc == 4) && ((strcmp (argv[3], "PK") == 0) || (strcmp (argv[3], "pk") == 0))) { DumpInfo (EFI_PLATFORM_KEY_NAME, &gEfiGlobalVariableGuid); return STATUS_SUCCESS; } else if ((argc == 4) && ((strcmp (argv[3], "KEK") == 0) || (strcmp (argv[3], "kek") == 0))) { DumpInfo (EFI_KEY_EXCHANGE_KEY_NAME, &gEfiGlobalVariableGuid); return STATUS_SUCCESS; } else if ((argc == 4) && ((strcmp (argv[3], "DB") == 0) || (strcmp (argv[3], "db") == 0))) { DumpInfo (EFI_IMAGE_SECURITY_DATABASE, &gEfiImageSecurityDatabaseGuid); return STATUS_SUCCESS; } else if ((argc == 4) && ((strcmp (argv[3], "DBX") == 0) || (strcmp (argv[3], "dbx") == 0))) { DumpInfo (EFI_IMAGE_SECURITY_DATABASE1, &gEfiImageSecurityDatabaseGuid); return STATUS_SUCCESS; } else if ((argc == 4) && ((strcmp (argv[3], "DBT") == 0) || (strcmp (argv[3], "dbt") == 0))) { DumpInfo (EFI_IMAGE_SECURITY_DATABASE2, &gEfiImageSecurityDatabaseGuid); return STATUS_SUCCESS; } else if ((argc == 4) && ((strcmp (argv[3], "ALL") == 0) || (strcmp (argv[3], "all") == 0))) { DumpVariableList (&mVarListEntry); return STATUS_SUCCESS; } Error (NULL, 0, 0, "INFO: Unknown SubCommand - ", argv[3]); return STATUS_ERROR; } STATUS ProcessKey ( IN INTN argc, IN CHAR8 **argv ) { if ((strcmp (argv[2], "INFO") == 0) || (strcmp (argv[2], "info") == 0)) { return ProcessInfo (argc, argv); } if ((strcmp (argv[3], "VAR") == 0) || (strcmp (argv[3], "var") == 0)) { return ProcessVar (argc, argv); } else if ((strcmp (argv[3], "PK") == 0) || (strcmp (argv[3], "pk") == 0)) { return ProcessPk (argc, argv); } else if ((strcmp (argv[3], "KEK") == 0) || (strcmp (argv[3], "kek") == 0)) { return ProcessKek (argc, argv); } else if ((strcmp (argv[3], "DB") == 0) || (strcmp (argv[3], "db") == 0)) { return ProcessDb (argc, argv, 0); } else if ((strcmp (argv[3], "DBX") == 0) || (strcmp (argv[3], "dbx") == 0)) { return ProcessDb (argc, argv, 1); } else if ((strcmp (argv[3], "DBT") == 0) || (strcmp (argv[3], "dbt") == 0)) { return ProcessDb (argc, argv, 2); } Error (NULL, 0, 0, "KeyEnroll: Unknown Command - ", argv[3]); return STATUS_ERROR; } STATUS KeyEnroll ( IN INTN argc, IN CHAR8 **argv ) /*++ Routine Description: Main function for KeyEnroll. Arguments: argc - Number of command line parameters. argv - Array of pointers to parameter strings. Returns: STATUS_SUCCESS - Utility exits successfully. STATUS_ERROR - Some error occurred during execution. --*/ { UINT32 VariableFvFileSize; UINT8 *VariableFvFileBuffer; STATUS Status; UINT8 *FdFileBuffer; UINT32 FdFileSize; EFI_FIRMWARE_VOLUME_HEADER *VarAddr; VOID *VariableStoreHeader; VARIABLE_TYPE VariableType; // // Step 1: Read InputFvRecovery.fv data // Status = ReadInputFile (argv[1], &FdFileBuffer, &FdFileSize, NULL); if (Status != STATUS_SUCCESS) { Error (NULL, 0, 0, "Unable to open file", "%s", argv[1]); return Status; } // // Get Variable information // VariableFvFileSize = GetVariableInfoFromFd (FdFileBuffer, FdFileSize, &VariableFvFileBuffer); if ((VariableFvFileSize == 0) || (VariableFvFileBuffer == NULL)) { Error (NULL, 0, 0, "Variable FV not found in Fd file!", NULL); free ((VOID *)FdFileBuffer); return STATUS_ERROR; } gEfiFdInfo.EfiVariableAddr = (UINTN)VariableFvFileBuffer; VarAddr = (EFI_FIRMWARE_VOLUME_HEADER *) gEfiFdInfo.EfiVariableAddr; VariableStoreHeader = (VOID *)((UINT8 *)VarAddr + VarAddr->HeaderLength); VariableType = CheckVarStore (VariableStoreHeader); switch (VariableType) { case VariableTypeTimeBasedAuth: printf ("VaraibleFv: Time-Based AuthVariable\n"); break; case VariableTypeCountBasedAuth: printf ("VaraibleFv: Monotonic-Based AuthVariable - unsupported\n"); return STATUS_ERROR; break; case VariableTypeNormal: printf ("VaraibleFv: Normal Variable - unsupported\n"); return STATUS_ERROR; break; default: printf ("VaraibleFv: Unknown - unsupported\n"); return STATUS_ERROR; } InitializeListHead (&mVarListEntry); Status = EfiVarAndListExchange (TRUE, &mVarListEntry); if (Status != STATUS_SUCCESS) { Error (NULL, 0, 0, "EfiVarAndListExchange (VarToList) Error!", NULL); return Status; } // // Construct Key // Status = ProcessKey (argc, argv); if (Status != STATUS_SUCCESS) { Error (NULL, 0, 0, "ProcessKey fail!", NULL); free ((VOID *)FdFileBuffer); return STATUS_ERROR; } if ((strcmp (argv[2], "INFO") == 0) || (strcmp (argv[2], "info") == 0)) { return STATUS_SUCCESS; } Status = EfiVarAndListExchange (FALSE, &mVarListEntry); if (Status != STATUS_SUCCESS) { Error (NULL, 0, 0, "EfiVarAndListExchange (ListToVar) Error!", NULL); return Status; } DestroyAllStorage (&mVarListEntry); // // Step 5: Write OutputFvRecovery.fv data // Status = WriteOutputFile (argv[2], FdFileBuffer, FdFileSize); free ((VOID *)FdFileBuffer); if (Status != STATUS_SUCCESS) { Error (NULL, 0, 0, "Unable to write file", "%s", argv[2]); return Status; } printf ("Write Variable Successfully!\n"); return Status; } int main ( int argc, char **argv ) /*++ Routine Description: Main function. Arguments: argc - Number of command line parameters. argv - Array of pointers to parameter strings. Returns: STATUS_SUCCESS - Utility exits successfully. STATUS_ERROR - Some error occurred during execution. --*/ { SetUtilityName (UTILITY_NAME); mIgnoreTimeStamp = 0; // // Display utility information // PrintUtilityInfo (); // // Verify the correct number of arguments // if (argc >= MIN_ARGS) { if (strcmp(argv[argc-1],"-nt") == 0){ argc -= 1; mIgnoreTimeStamp = 1; } return KeyEnroll (argc, argv); } else { Error (NULL, 0, 0, "invalid number of input parameters specified", NULL); PrintUsage (); return STATUS_ERROR; } } unsigned int xtoi ( char *str ) /*++ Routine Description: Convert hex string to uint Arguments: Str - The string Returns: --*/ { unsigned int u; char c; unsigned int m; if (str == NULL) { return 0; } m = (unsigned int) -1 >> 4; // // skip preceeding white space // while (*str && *str == ' ') { str += 1; } // // skip preceeding zeros // while (*str && *str == '0') { str += 1; } // // skip preceeding white space // if (*str && (*str == 'x' || *str == 'X')) { str += 1; } // // convert hex digits // u = 0; c = *(str++); while (c) { if (c >= 'a' && c <= 'f') { c -= 'a' - 'A'; } if ((c >= '0' && c <= '9') || (c >= 'A' && c <= 'F')) { if (u > m) { return (unsigned int) -1; } u = (u << 4) | (c - (c >= 'A' ? 'A' - 10 : '0')); } else { // // Let aplication exit immediately // Error (NULL, 0, 0, "Hex value is expected!", NULL); exit (0); break; } c = *(str++); } return u; } INTN KeyEnrollStrCmp ( IN CONST CHAR16 *FirstString, IN CONST CHAR16 *SecondString ) { while ((*FirstString != L'\0') && (*FirstString == *SecondString)) { FirstString++; SecondString++; } return *FirstString - *SecondString; } UINTN KeyEnrollStrLen ( IN CONST CHAR16 *String ) { UINTN Length; if (String == NULL) { return 0; } for (Length = 0; *String != L'\0'; String++, Length++) { ; } return Length; } UINTN KeyEnrollStrSize ( IN CONST CHAR16 *String ) { return (KeyEnrollStrLen (String) + 1) * sizeof(CHAR16); } INTN CompareMem ( IN CONST VOID *DestinationBuffer, IN CONST VOID *SourceBuffer, IN UINTN Length ) { while ((--Length != 0) && (*(INT8*)DestinationBuffer == *(INT8*)SourceBuffer)) { DestinationBuffer = (INT8*)DestinationBuffer + 1; SourceBuffer = (INT8*)SourceBuffer + 1; } return (INTN)*(UINT8*)DestinationBuffer - (INTN)*(UINT8*)SourceBuffer; } LIST_ENTRY * EFIAPI GetFirstNode ( IN CONST LIST_ENTRY *List ) { return List->ForwardLink; } BOOLEAN EFIAPI IsNull ( IN CONST LIST_ENTRY *List, IN CONST LIST_ENTRY *Node ) { return (BOOLEAN)(Node == List); } LIST_ENTRY * EFIAPI GetNextNode ( IN CONST LIST_ENTRY *List, IN CONST LIST_ENTRY *Node ) { return Node->ForwardLink; } LIST_ENTRY * EFIAPI InitializeListHead ( IN OUT LIST_ENTRY *ListHead ) { ListHead->ForwardLink = ListHead; ListHead->BackLink = ListHead; return ListHead; } LIST_ENTRY * EFIAPI InsertTailList ( IN OUT LIST_ENTRY *ListHead, IN OUT LIST_ENTRY *Entry ) { Entry->ForwardLink = ListHead; Entry->BackLink = ListHead->BackLink; Entry->BackLink->ForwardLink = Entry; ListHead->BackLink = Entry; return ListHead; } LIST_ENTRY * EFIAPI RemoveEntryList ( IN CONST LIST_ENTRY *Entry ) { Entry->ForwardLink->BackLink = Entry->BackLink; Entry->BackLink->ForwardLink = Entry->ForwardLink; return Entry->ForwardLink; } BOOLEAN EFIAPI IsListEmpty ( IN CONST LIST_ENTRY *ListHead ) { return (BOOLEAN)(ListHead->ForwardLink == ListHead); }