]> git.proxmox.com Git - mirror_edk2.git/blobdiff - OvmfPkg/EnrollDefaultKeys/EnrollDefaultKeys.c
OvmfPkg/EnrollDefaultKeys: enroll PK/KEK1 from the Type 11 SMBIOS table
[mirror_edk2.git] / OvmfPkg / EnrollDefaultKeys / EnrollDefaultKeys.c
index 9c4a0f06fb4d6b22dfd88571daad2e503ef07e88..b7b2e424c59e8cac7b6496faa7e3a93960b91749 100644 (file)
 #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
@@ -358,21 +533,38 @@ ShellAppMain (
   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
@@ -388,7 +580,7 @@ ShellAppMain (
     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
@@ -403,7 +595,7 @@ ShellAppMain (
              mMicrosoftUefiCa, mSizeOfMicrosoftUefiCa, &gMicrosoftVendorGuid,\r
              NULL);\r
   if (EFI_ERROR (Status)) {\r
-    return 1;\r
+    goto FreePkKek1;\r
   }\r
 \r
   //\r
@@ -416,7 +608,7 @@ ShellAppMain (
              mSha256OfDevNull, mSizeOfSha256OfDevNull, &gEfiCallerIdGuid,\r
              NULL);\r
   if (EFI_ERROR (Status)) {\r
-    return 1;\r
+    goto FreePkKek1;\r
   }\r
 \r
   //\r
@@ -426,11 +618,11 @@ ShellAppMain (
              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
@@ -440,10 +632,10 @@ ShellAppMain (
              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
@@ -457,7 +649,7 @@ ShellAppMain (
   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
@@ -493,7 +685,7 @@ ShellAppMain (
   //\r
   Status = GetSettings (&Settings);\r
   if (EFI_ERROR (Status)) {\r
-    return 1;\r
+    goto FreePkKek1;\r
   }\r
   PrintSettings (&Settings);\r
 \r
@@ -501,9 +693,14 @@ ShellAppMain (
       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