]> git.proxmox.com Git - mirror_edk2.git/commitdiff
OvmfPkg/EnrollDefaultKeys: enroll PK/KEK1 from the Type 11 SMBIOS table
authorLaszlo Ersek <lersek@redhat.com>
Thu, 25 Apr 2019 21:15:14 +0000 (23:15 +0200)
committerLaszlo Ersek <lersek@redhat.com>
Tue, 30 Apr 2019 12:26:46 +0000 (14:26 +0200)
Disconnect the certificate that is enrolled as both Platform Key and first
Key Exchange Key from Red Hat: expect the hypervisor to specify it, as
part of SMBIOS.

Example usage with QEMU:

* Generate self-signed X509 certificate:

  openssl req \
    -x509 \
    -newkey rsa:2048 \
    -outform PEM \
    -keyout PkKek1.private.key \
    -out PkKek1.pem

  (where "PEM" simply means "DER + base64 + header + footer").

* Strip the header, footer, and newline characters; prepend the
  application prefix:

  sed \
    -e 's/^-----BEGIN CERTIFICATE-----$/4e32566d-8e9e-4f52-81d3-5bb9715f9727:/' \
    -e '/^-----END CERTIFICATE-----$/d' \
    PkKek1.pem \
  | tr -d '\n' \
  > PkKek1.oemstr

* Pass the certificate to EnrollDefaultKeys with the following QEMU
  option:

  -smbios type=11,value="$(< PkKek1.oemstr)"

  (Note: for the above option to work correctly, a QEMU version is needed
  that includes commit 950c4e6c94b1 ("opts: don't silently truncate long
  option values", 2018-05-09). The first upstream release with that commit
  was v3.0.0.

  Once <https://bugs.launchpad.net/qemu/+bug/1826200> is fixed, QEMU will
  learn to read the file directly; passing the blob on the command will be
  necessary no more.)

Cc: Anthony Perard <anthony.perard@citrix.com>
Cc: Ard Biesheuvel <ard.biesheuvel@linaro.org>
Cc: Jordan Justen <jordan.l.justen@intel.com>
Cc: Julien Grall <julien.grall@arm.com>
Bugzilla: https://bugzilla.tianocore.org/show_bug.cgi?id=1747
Signed-off-by: Laszlo Ersek <lersek@redhat.com>
Reviewed-by: Philippe Mathieu-Daude <philmd@redhat.com>
Acked-by: Ard Biesheuvel <ard.biesheuvel@linaro.org>
Reviewed-by: Gary Lin <glin@suse.com>
OvmfPkg/EnrollDefaultKeys/EnrollDefaultKeys.c
OvmfPkg/EnrollDefaultKeys/EnrollDefaultKeys.inf

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
index 28db52586a9bd4e15aac6d3fec6a67a2df8732e4..184f7972d52d33985d4355f379e90ef779ea0457 100644 (file)
   gEfiImageSecurityDatabaseGuid\r
   gEfiSecureBootEnableDisableGuid\r
   gMicrosoftVendorGuid\r
+  gOvmfPkKek1AppPrefixGuid\r
+\r
+[Protocols]\r
+  gEfiSmbiosProtocolGuid ## CONSUMES\r
 \r
 [LibraryClasses]\r
+  BaseLib\r
   BaseMemoryLib\r
   DebugLib\r
   MemoryAllocationLib\r
+  PrintLib\r
   ShellCEntryLib\r
+  UefiBootServicesTableLib\r
   UefiLib\r
   UefiRuntimeServicesTableLib\r