#include <Guid/GlobalVariable.h> // EFI_SETUP_MODE_NAME\r
#include <Guid/ImageAuthentication.h> // EFI_IMAGE_SECURITY_DATABASE\r
#include <Guid/MicrosoftVendor.h> // gMicrosoftVendorGuid\r
+#include <Guid/OvmfPkKek1AppPrefix.h> // gOvmfPkKek1AppPrefixGuid\r
+#include <IndustryStandard/SmBios.h> // SMBIOS_HANDLE_PI_RESERVED\r
+#include <Library/BaseLib.h> // GUID_STRING_LENGTH\r
#include <Library/BaseMemoryLib.h> // CopyGuid()\r
#include <Library/DebugLib.h> // ASSERT()\r
#include <Library/MemoryAllocationLib.h> // FreePool()\r
+#include <Library/PrintLib.h> // AsciiSPrint()\r
#include <Library/ShellCEntryLib.h> // ShellAppMain()\r
+#include <Library/UefiBootServicesTableLib.h> // gBS\r
#include <Library/UefiLib.h> // AsciiPrint()\r
#include <Library/UefiRuntimeServicesTableLib.h> // gRT\r
+#include <Protocol/Smbios.h> // EFI_SMBIOS_PROTOCOL\r
\r
#include "EnrollDefaultKeys.h"\r
\r
\r
+/**\r
+ Fetch the X509 certificate (to be used as Platform Key and first Key Exchange\r
+ Key) from SMBIOS.\r
+\r
+ @param[out] PkKek1 The X509 certificate in DER encoding from the\r
+ hypervisor, to be enrolled as PK and first KEK\r
+ entry. On success, the caller is responsible for\r
+ releasing PkKek1 with FreePool().\r
+\r
+ @param[out] SizeOfPkKek1 The size of PkKek1 in bytes.\r
+\r
+ @retval EFI_SUCCESS PkKek1 and SizeOfPkKek1 have been set\r
+ successfully.\r
+\r
+ @retval EFI_NOT_FOUND An OEM String matching\r
+ OVMF_PK_KEK1_APP_PREFIX_GUID has not been\r
+ found.\r
+\r
+ @retval EFI_PROTOCOL_ERROR In the OEM String matching\r
+ OVMF_PK_KEK1_APP_PREFIX_GUID, the certificate\r
+ is empty, or it has invalid base64 encoding.\r
+\r
+ @retval EFI_OUT_OF_RESOURCES Memory allocation failed.\r
+\r
+ @return Error codes from gBS->LocateProtocol().\r
+**/\r
+STATIC\r
+EFI_STATUS\r
+GetPkKek1 (\r
+ OUT UINT8 **PkKek1,\r
+ OUT UINTN *SizeOfPkKek1\r
+ )\r
+{\r
+ CONST CHAR8 *Base64Cert;\r
+ CHAR8 OvmfPkKek1AppPrefix[GUID_STRING_LENGTH + 1 + 1];\r
+ EFI_STATUS Status;\r
+ EFI_SMBIOS_PROTOCOL *Smbios;\r
+ EFI_SMBIOS_HANDLE Handle;\r
+ EFI_SMBIOS_TYPE Type;\r
+ EFI_SMBIOS_TABLE_HEADER *Header;\r
+ SMBIOS_TABLE_TYPE11 *OemStringsTable;\r
+ UINTN Base64CertLen;\r
+ UINTN DecodedCertSize;\r
+ UINT8 *DecodedCert;\r
+\r
+ Base64Cert = NULL;\r
+\r
+ //\r
+ // Format the application prefix, for OEM String matching.\r
+ //\r
+ AsciiSPrint (OvmfPkKek1AppPrefix, sizeof OvmfPkKek1AppPrefix, "%g:",\r
+ &gOvmfPkKek1AppPrefixGuid);\r
+\r
+ //\r
+ // Scan all "OEM Strings" tables.\r
+ //\r
+ Status = gBS->LocateProtocol (&gEfiSmbiosProtocolGuid, NULL,\r
+ (VOID **)&Smbios);\r
+ if (EFI_ERROR (Status)) {\r
+ AsciiPrint ("error: failed to locate EFI_SMBIOS_PROTOCOL: %r\n", Status);\r
+ return Status;\r
+ }\r
+\r
+ Handle = SMBIOS_HANDLE_PI_RESERVED;\r
+ Type = SMBIOS_TYPE_OEM_STRINGS;\r
+ for (Status = Smbios->GetNext (Smbios, &Handle, &Type, &Header, NULL);\r
+ !EFI_ERROR (Status);\r
+ Status = Smbios->GetNext (Smbios, &Handle, &Type, &Header, NULL)) {\r
+ CONST CHAR8 *OemString;\r
+ UINTN Idx;\r
+\r
+ if (Header->Length < sizeof *OemStringsTable) {\r
+ //\r
+ // Malformed table header, skip to next.\r
+ //\r
+ continue;\r
+ }\r
+ OemStringsTable = (SMBIOS_TABLE_TYPE11 *)Header;\r
+\r
+ //\r
+ // Scan all strings in the unformatted area of the current "OEM Strings"\r
+ // table.\r
+ //\r
+ OemString = (CONST CHAR8 *)(OemStringsTable + 1);\r
+ for (Idx = 0; Idx < OemStringsTable->StringCount; ++Idx) {\r
+ CHAR8 CandidatePrefix[sizeof OvmfPkKek1AppPrefix];\r
+\r
+ //\r
+ // NUL-terminate the candidate prefix for case-insensitive comparison.\r
+ //\r
+ AsciiStrnCpyS (CandidatePrefix, sizeof CandidatePrefix, OemString,\r
+ GUID_STRING_LENGTH + 1);\r
+ if (AsciiStriCmp (OvmfPkKek1AppPrefix, CandidatePrefix) == 0) {\r
+ //\r
+ // The current string matches the prefix.\r
+ //\r
+ Base64Cert = OemString + GUID_STRING_LENGTH + 1;\r
+ break;\r
+ }\r
+ OemString += AsciiStrSize (OemString);\r
+ }\r
+\r
+ if (Idx < OemStringsTable->StringCount) {\r
+ //\r
+ // The current table has a matching string.\r
+ //\r
+ break;\r
+ }\r
+ }\r
+\r
+ if (EFI_ERROR (Status)) {\r
+ //\r
+ // No table with a matching string has been found.\r
+ //\r
+ AsciiPrint ("error: OEM String with app prefix %g not found: %r\n",\r
+ &gOvmfPkKek1AppPrefixGuid, Status);\r
+ return EFI_NOT_FOUND;\r
+ }\r
+\r
+ ASSERT (Base64Cert != NULL);\r
+ Base64CertLen = AsciiStrLen (Base64Cert);\r
+\r
+ //\r
+ // Verify the base64 encoding, and determine the decoded size.\r
+ //\r
+ DecodedCertSize = 0;\r
+ Status = Base64Decode (Base64Cert, Base64CertLen, NULL, &DecodedCertSize);\r
+ switch (Status) {\r
+ case EFI_BUFFER_TOO_SMALL:\r
+ if (DecodedCertSize > 0) {\r
+ break;\r
+ }\r
+ //\r
+ // Fall through: the above Base64Decode() call is ill-specified in BaseLib\r
+ // if Source decodes to zero bytes (for example if it consists of ignored\r
+ // whitespace only).\r
+ //\r
+ case EFI_SUCCESS:\r
+ AsciiPrint ("error: empty certificate after app prefix %g\n",\r
+ &gOvmfPkKek1AppPrefixGuid);\r
+ return EFI_PROTOCOL_ERROR;\r
+ default:\r
+ AsciiPrint ("error: invalid base64 string after app prefix %g\n",\r
+ &gOvmfPkKek1AppPrefixGuid);\r
+ return EFI_PROTOCOL_ERROR;\r
+ }\r
+\r
+ //\r
+ // Allocate the output buffer.\r
+ //\r
+ DecodedCert = AllocatePool (DecodedCertSize);\r
+ if (DecodedCert == NULL) {\r
+ AsciiPrint ("error: failed to allocate memory\n");\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+\r
+ //\r
+ // Decoding will succeed at this point.\r
+ //\r
+ Status = Base64Decode (Base64Cert, Base64CertLen, DecodedCert,\r
+ &DecodedCertSize);\r
+ ASSERT_EFI_ERROR (Status);\r
+\r
+ *PkKek1 = DecodedCert;\r
+ *SizeOfPkKek1 = DecodedCertSize;\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+\r
/**\r
Enroll a set of certificates in a global variable, overwriting it.\r
\r
IN CHAR16 **Argv\r
)\r
{\r
+ INTN RetVal;\r
EFI_STATUS Status;\r
SETTINGS Settings;\r
+ UINT8 *PkKek1;\r
+ UINTN SizeOfPkKek1;\r
+\r
+ //\r
+ // Prepare for failure.\r
+ //\r
+ RetVal = 1;\r
\r
//\r
// If we're not in Setup Mode, we can't do anything.\r
//\r
Status = GetSettings (&Settings);\r
if (EFI_ERROR (Status)) {\r
- return 1;\r
+ return RetVal;\r
}\r
PrintSettings (&Settings);\r
\r
if (Settings.SetupMode != 1) {\r
AsciiPrint ("error: already in User Mode\n");\r
- return 1;\r
+ return RetVal;\r
+ }\r
+\r
+ //\r
+ // Fetch the X509 certificate (to be used as Platform Key and first Key\r
+ // Exchange Key) from SMBIOS.\r
+ //\r
+ Status = GetPkKek1 (&PkKek1, &SizeOfPkKek1);\r
+ if (EFI_ERROR (Status)) {\r
+ return RetVal;\r
}\r
\r
//\r
if (EFI_ERROR (Status)) {\r
AsciiPrint ("error: SetVariable(\"%s\", %g): %r\n", EFI_CUSTOM_MODE_NAME,\r
&gEfiCustomModeEnableGuid, Status);\r
- return 1;\r
+ goto FreePkKek1;\r
}\r
}\r
\r
mMicrosoftUefiCa, mSizeOfMicrosoftUefiCa, &gMicrosoftVendorGuid,\r
NULL);\r
if (EFI_ERROR (Status)) {\r
- return 1;\r
+ goto FreePkKek1;\r
}\r
\r
//\r
mSha256OfDevNull, mSizeOfSha256OfDevNull, &gEfiCallerIdGuid,\r
NULL);\r
if (EFI_ERROR (Status)) {\r
- return 1;\r
+ goto FreePkKek1;\r
}\r
\r
//\r
EFI_KEY_EXCHANGE_KEY_NAME,\r
&gEfiGlobalVariableGuid,\r
&gEfiCertX509Guid,\r
- mRedHatPkKek1, mSizeOfRedHatPkKek1, &gEfiCallerIdGuid,\r
+ PkKek1, SizeOfPkKek1, &gEfiCallerIdGuid,\r
mMicrosoftKek, mSizeOfMicrosoftKek, &gMicrosoftVendorGuid,\r
NULL);\r
if (EFI_ERROR (Status)) {\r
- return 1;\r
+ goto FreePkKek1;\r
}\r
\r
//\r
EFI_PLATFORM_KEY_NAME,\r
&gEfiGlobalVariableGuid,\r
&gEfiCertX509Guid,\r
- mRedHatPkKek1, mSizeOfRedHatPkKek1, &gEfiGlobalVariableGuid,\r
+ PkKek1, SizeOfPkKek1, &gEfiGlobalVariableGuid,\r
NULL);\r
if (EFI_ERROR (Status)) {\r
- return 1;\r
+ goto FreePkKek1;\r
}\r
\r
//\r
if (EFI_ERROR (Status)) {\r
AsciiPrint ("error: SetVariable(\"%s\", %g): %r\n", EFI_CUSTOM_MODE_NAME,\r
&gEfiCustomModeEnableGuid, Status);\r
- return 1;\r
+ goto FreePkKek1;\r
}\r
\r
//\r
//\r
Status = GetSettings (&Settings);\r
if (EFI_ERROR (Status)) {\r
- return 1;\r
+ goto FreePkKek1;\r
}\r
PrintSettings (&Settings);\r
\r
Settings.SecureBootEnable != 1 || Settings.CustomMode != 0 ||\r
Settings.VendorKeys != 0) {\r
AsciiPrint ("error: unexpected\n");\r
- return 1;\r
+ goto FreePkKek1;\r
}\r
\r
AsciiPrint ("info: success\n");\r
- return 0;\r
+ RetVal = 0;\r
+\r
+FreePkKek1:\r
+ FreePool (PkKek1);\r
+\r
+ return RetVal;\r
}\r