/** @file
  Enroll default PK, KEK, db, dbx.

  Copyright (C) 2014-2019, Red Hat, Inc.

  SPDX-License-Identifier: BSD-2-Clause-Patent
**/
#include <Guid/AuthenticatedVariableFormat.h>    // gEfiCustomModeEnableGuid
#include <Guid/GlobalVariable.h>                 // EFI_SETUP_MODE_NAME
#include <Guid/ImageAuthentication.h>            // EFI_IMAGE_SECURITY_DATABASE
#include <Guid/MicrosoftVendor.h>                // gMicrosoftVendorGuid
#include <Guid/OvmfPkKek1AppPrefix.h>            // gOvmfPkKek1AppPrefixGuid
#include <IndustryStandard/SmBios.h>             // SMBIOS_HANDLE_PI_RESERVED
#include <Library/BaseLib.h>                     // GUID_STRING_LENGTH
#include <Library/BaseMemoryLib.h>               // CopyGuid()
#include <Library/DebugLib.h>                    // ASSERT()
#include <Library/MemoryAllocationLib.h>         // FreePool()
#include <Library/PrintLib.h>                    // AsciiSPrint()
#include <Library/ShellCEntryLib.h>              // ShellAppMain()
#include <Library/UefiBootServicesTableLib.h>    // gBS
#include <Library/UefiLib.h>                     // AsciiPrint()
#include <Library/UefiRuntimeServicesTableLib.h> // gRT
#include <Protocol/Smbios.h>                     // EFI_SMBIOS_PROTOCOL

#include "EnrollDefaultKeys.h"

/**
  Fetch the X509 certificate (to be used as Platform Key and first Key Exchange
  Key) from SMBIOS.

  @param[out] PkKek1        The X509 certificate in DER encoding from the
                            hypervisor, to be enrolled as PK and first KEK
                            entry. On success, the caller is responsible for
                            releasing PkKek1 with FreePool().

  @param[out] SizeOfPkKek1  The size of PkKek1 in bytes.

  @retval EFI_SUCCESS           PkKek1 and SizeOfPkKek1 have been set
                                successfully.

  @retval EFI_NOT_FOUND         An OEM String matching
                                OVMF_PK_KEK1_APP_PREFIX_GUID has not been
                                found.

  @retval EFI_PROTOCOL_ERROR    In the OEM String matching
                                OVMF_PK_KEK1_APP_PREFIX_GUID, the certificate
                                is empty, or it has invalid base64 encoding.

  @retval EFI_OUT_OF_RESOURCES  Memory allocation failed.

  @return                       Error codes from gBS->LocateProtocol().
**/
STATIC
EFI_STATUS
GetPkKek1 (
  OUT UINT8  **PkKek1,
  OUT UINTN  *SizeOfPkKek1
  )
{
  CONST CHAR8              *Base64Cert;
  CHAR8                    OvmfPkKek1AppPrefix[GUID_STRING_LENGTH + 1 + 1];
  EFI_STATUS               Status;
  EFI_SMBIOS_PROTOCOL      *Smbios;
  EFI_SMBIOS_HANDLE        Handle;
  EFI_SMBIOS_TYPE          Type;
  EFI_SMBIOS_TABLE_HEADER  *Header;
  SMBIOS_TABLE_TYPE11      *OemStringsTable;
  UINTN                    Base64CertLen;
  UINTN                    DecodedCertSize;
  UINT8                    *DecodedCert;

  Base64Cert = NULL;

  //
  // Format the application prefix, for OEM String matching.
  //
  AsciiSPrint (
    OvmfPkKek1AppPrefix,
    sizeof OvmfPkKek1AppPrefix,
    "%g:",
    &gOvmfPkKek1AppPrefixGuid
    );

  //
  // Scan all "OEM Strings" tables.
  //
  Status = gBS->LocateProtocol (
                  &gEfiSmbiosProtocolGuid,
                  NULL,
                  (VOID **)&Smbios
                  );
  if (EFI_ERROR (Status)) {
    AsciiPrint ("error: failed to locate EFI_SMBIOS_PROTOCOL: %r\n", Status);
    return Status;
  }

  Handle = SMBIOS_HANDLE_PI_RESERVED;
  Type   = SMBIOS_TYPE_OEM_STRINGS;
  for (Status = Smbios->GetNext (Smbios, &Handle, &Type, &Header, NULL);
       !EFI_ERROR (Status);
       Status = Smbios->GetNext (Smbios, &Handle, &Type, &Header, NULL))
  {
    CONST CHAR8  *OemString;
    UINTN        Idx;

    if (Header->Length < sizeof *OemStringsTable) {
      //
      // Malformed table header, skip to next.
      //
      continue;
    }

    OemStringsTable = (SMBIOS_TABLE_TYPE11 *)Header;

    //
    // Scan all strings in the unformatted area of the current "OEM Strings"
    // table.
    //
    OemString = (CONST CHAR8 *)(OemStringsTable + 1);
    for (Idx = 0; Idx < OemStringsTable->StringCount; ++Idx) {
      CHAR8  CandidatePrefix[sizeof OvmfPkKek1AppPrefix];

      //
      // NUL-terminate the candidate prefix for case-insensitive comparison.
      //
      AsciiStrnCpyS (
        CandidatePrefix,
        sizeof CandidatePrefix,
        OemString,
        GUID_STRING_LENGTH + 1
        );
      if (AsciiStriCmp (OvmfPkKek1AppPrefix, CandidatePrefix) == 0) {
        //
        // The current string matches the prefix.
        //
        Base64Cert = OemString + GUID_STRING_LENGTH + 1;
        break;
      }

      OemString += AsciiStrSize (OemString);
    }

    if (Idx < OemStringsTable->StringCount) {
      //
      // The current table has a matching string.
      //
      break;
    }
  }

  if (EFI_ERROR (Status)) {
    //
    // No table with a matching string has been found.
    //
    AsciiPrint (
      "error: OEM String with app prefix %g not found: %r\n",
      &gOvmfPkKek1AppPrefixGuid,
      Status
      );
    return EFI_NOT_FOUND;
  }

  ASSERT (Base64Cert != NULL);
  Base64CertLen = AsciiStrLen (Base64Cert);

  //
  // Verify the base64 encoding, and determine the decoded size.
  //
  DecodedCertSize = 0;
  Status          = Base64Decode (Base64Cert, Base64CertLen, NULL, &DecodedCertSize);
  switch (Status) {
    case EFI_BUFFER_TOO_SMALL:
      ASSERT (DecodedCertSize > 0);
      break;
    case EFI_SUCCESS:
      AsciiPrint (
        "error: empty certificate after app prefix %g\n",
        &gOvmfPkKek1AppPrefixGuid
        );
      return EFI_PROTOCOL_ERROR;
    default:
      AsciiPrint (
        "error: invalid base64 string after app prefix %g\n",
        &gOvmfPkKek1AppPrefixGuid
        );
      return EFI_PROTOCOL_ERROR;
  }

  //
  // Allocate the output buffer.
  //
  DecodedCert = AllocatePool (DecodedCertSize);
  if (DecodedCert == NULL) {
    AsciiPrint ("error: failed to allocate memory\n");
    return EFI_OUT_OF_RESOURCES;
  }

  //
  // Decoding will succeed at this point.
  //
  Status = Base64Decode (
             Base64Cert,
             Base64CertLen,
             DecodedCert,
             &DecodedCertSize
             );
  ASSERT_EFI_ERROR (Status);

  *PkKek1       = DecodedCert;
  *SizeOfPkKek1 = DecodedCertSize;
  return EFI_SUCCESS;
}

/**
  Enroll a set of certificates in a global variable, overwriting it.

  The variable will be rewritten with NV+BS+RT+AT attributes.

  @param[in] VariableName  The name of the variable to overwrite.

  @param[in] VendorGuid    The namespace (ie. vendor GUID) of the variable to
                           overwrite.

  @param[in] CertType      The GUID determining the type of all the
                           certificates in the set that is passed in. For
                           example, gEfiCertX509Guid stands for DER-encoded
                           X.509 certificates, while gEfiCertSha256Guid stands
                           for SHA256 image hashes.

  @param[in] ...           A list of

                             IN CONST UINT8    *Cert,
                             IN UINTN          CertSize,
                             IN CONST EFI_GUID *OwnerGuid

                           triplets. If the first component of a triplet is
                           NULL, then the other two components are not
                           accessed, and processing is terminated. The list of
                           certificates is enrolled in the variable specified,
                           overwriting it. The OwnerGuid component identifies
                           the agent installing the certificate.

  @retval EFI_INVALID_PARAMETER  The triplet list is empty (ie. the first Cert
                                 value is NULL), or one of the CertSize values
                                 is 0, or one of the CertSize values would
                                 overflow the accumulated UINT32 data size.

  @retval EFI_OUT_OF_RESOURCES   Out of memory while formatting variable
                                 payload.

  @retval EFI_SUCCESS            Enrollment successful; the variable has been
                                 overwritten (or created).

  @return                        Error codes from gRT->GetTime() and
                                 gRT->SetVariable().
**/
STATIC
EFI_STATUS
EFIAPI
EnrollListOfCerts (
  IN CHAR16    *VariableName,
  IN EFI_GUID  *VendorGuid,
  IN EFI_GUID  *CertType,
  ...
  )
{
  UINTN             DataSize;
  SINGLE_HEADER     *SingleHeader;
  REPEATING_HEADER  *RepeatingHeader;
  VA_LIST           Marker;
  CONST UINT8       *Cert;
  EFI_STATUS        Status;
  UINT8             *Data;
  UINT8             *Position;

  Status = EFI_SUCCESS;

  //
  // compute total size first, for UINT32 range check, and allocation
  //
  DataSize = sizeof *SingleHeader;
  VA_START (Marker, CertType);
  for (Cert = VA_ARG (Marker, CONST UINT8 *);
       Cert != NULL;
       Cert = VA_ARG (Marker, CONST UINT8 *))
  {
    UINTN  CertSize;

    CertSize = VA_ARG (Marker, UINTN);
    (VOID)VA_ARG (Marker, CONST EFI_GUID *);

    if ((CertSize == 0) ||
        (CertSize > MAX_UINT32 - sizeof *RepeatingHeader) ||
        (DataSize > MAX_UINT32 - sizeof *RepeatingHeader - CertSize))
    {
      Status = EFI_INVALID_PARAMETER;
      break;
    }

    DataSize += sizeof *RepeatingHeader + CertSize;
  }

  VA_END (Marker);

  if (DataSize == sizeof *SingleHeader) {
    Status = EFI_INVALID_PARAMETER;
  }

  if (EFI_ERROR (Status)) {
    goto Out;
  }

  Data = AllocatePool (DataSize);
  if (Data == NULL) {
    Status = EFI_OUT_OF_RESOURCES;
    goto Out;
  }

  Position = Data;

  SingleHeader = (SINGLE_HEADER *)Position;
  Status       = gRT->GetTime (&SingleHeader->TimeStamp, NULL);
  if (EFI_ERROR (Status)) {
    goto FreeData;
  }

  SingleHeader->TimeStamp.Pad1       = 0;
  SingleHeader->TimeStamp.Nanosecond = 0;
  SingleHeader->TimeStamp.TimeZone   = 0;
  SingleHeader->TimeStamp.Daylight   = 0;
  SingleHeader->TimeStamp.Pad2       = 0;
 #if 0
  SingleHeader->dwLength = DataSize - sizeof SingleHeader->TimeStamp;
 #else
  //
  // This looks like a bug in edk2. According to the UEFI specification,
  // dwLength is "The length of the entire certificate, including the length of
  // the header, in bytes". That shouldn't stop right after CertType -- it
  // should include everything below it.
  //
  SingleHeader->dwLength = sizeof *SingleHeader
                           - sizeof SingleHeader->TimeStamp;
 #endif
  SingleHeader->wRevision        = 0x0200;
  SingleHeader->wCertificateType = WIN_CERT_TYPE_EFI_GUID;
  CopyGuid (&SingleHeader->CertType, &gEfiCertPkcs7Guid);
  Position += sizeof *SingleHeader;

  VA_START (Marker, CertType);
  for (Cert = VA_ARG (Marker, CONST UINT8 *);
       Cert != NULL;
       Cert = VA_ARG (Marker, CONST UINT8 *))
  {
    UINTN           CertSize;
    CONST EFI_GUID  *OwnerGuid;

    CertSize  = VA_ARG (Marker, UINTN);
    OwnerGuid = VA_ARG (Marker, CONST EFI_GUID *);

    RepeatingHeader = (REPEATING_HEADER *)Position;
    CopyGuid (&RepeatingHeader->SignatureType, CertType);
    RepeatingHeader->SignatureListSize =
      (UINT32)(sizeof *RepeatingHeader + CertSize);
    RepeatingHeader->SignatureHeaderSize = 0;
    RepeatingHeader->SignatureSize       =
      (UINT32)(sizeof RepeatingHeader->SignatureOwner + CertSize);
    CopyGuid (&RepeatingHeader->SignatureOwner, OwnerGuid);
    Position += sizeof *RepeatingHeader;

    CopyMem (Position, Cert, CertSize);
    Position += CertSize;
  }

  VA_END (Marker);

  ASSERT (Data + DataSize == Position);

  Status = gRT->SetVariable (
                  VariableName,
                  VendorGuid,
                  (EFI_VARIABLE_NON_VOLATILE |
                   EFI_VARIABLE_BOOTSERVICE_ACCESS |
                   EFI_VARIABLE_RUNTIME_ACCESS |
                   EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS),
                  DataSize,
                  Data
                  );

FreeData:
  FreePool (Data);

Out:
  if (EFI_ERROR (Status)) {
    AsciiPrint (
      "error: %a(\"%s\", %g): %r\n",
      __func__,
      VariableName,
      VendorGuid,
      Status
      );
  }

  return Status;
}

/**
  Read a UEFI variable into a caller-allocated buffer, enforcing an exact size.

  @param[in] VariableName  The name of the variable to read; passed to
                           gRT->GetVariable().

  @param[in] VendorGuid    The vendor (namespace) GUID of the variable to read;
                           passed to gRT->GetVariable().

  @param[out] Data         The caller-allocated buffer that is supposed to
                           receive the variable's contents. On error, the
                           contents of Data are indeterminate.

  @param[in] DataSize      The size in bytes that the caller requires the UEFI
                           variable to have. The caller is responsible for
                           providing room for DataSize bytes in Data.

  @param[in] AllowMissing  If FALSE, the variable is required to exist. If
                           TRUE, the variable is permitted to be missing.

  @retval EFI_SUCCESS           The UEFI variable exists, has the required size
                                (DataSize), and has been read into Data.

  @retval EFI_SUCCESS           The UEFI variable doesn't exist, and
                                AllowMissing is TRUE. DataSize bytes in Data
                                have been zeroed out.

  @retval EFI_NOT_FOUND         The UEFI variable doesn't exist, and
                                AllowMissing is FALSE.

  @retval EFI_BUFFER_TOO_SMALL  The UEFI variable exists, but its size is
                                greater than DataSize.

  @retval EFI_PROTOCOL_ERROR    The UEFI variable exists, but its size is
                                smaller than DataSize.

  @return                       Error codes propagated from gRT->GetVariable().
**/
STATIC
EFI_STATUS
GetExact (
  IN CHAR16    *VariableName,
  IN EFI_GUID  *VendorGuid,
  OUT VOID     *Data,
  IN UINTN     DataSize,
  IN BOOLEAN   AllowMissing
  )
{
  UINTN       Size;
  EFI_STATUS  Status;

  Size   = DataSize;
  Status = gRT->GetVariable (VariableName, VendorGuid, NULL, &Size, Data);
  if (EFI_ERROR (Status)) {
    if ((Status == EFI_NOT_FOUND) && AllowMissing) {
      ZeroMem (Data, DataSize);
      return EFI_SUCCESS;
    }

    AsciiPrint (
      "error: GetVariable(\"%s\", %g): %r\n",
      VariableName,
      VendorGuid,
      Status
      );
    return Status;
  }

  if (Size != DataSize) {
    AsciiPrint (
      "error: GetVariable(\"%s\", %g): expected size 0x%Lx, "
      "got 0x%Lx\n",
      VariableName,
      VendorGuid,
      (UINT64)DataSize,
      (UINT64)Size
      );
    return EFI_PROTOCOL_ERROR;
  }

  return EFI_SUCCESS;
}

/**
  Populate a SETTINGS structure from the underlying UEFI variables.

  The following UEFI variables are standard variables:
  - L"SetupMode"  (EFI_SETUP_MODE_NAME)
  - L"SecureBoot" (EFI_SECURE_BOOT_MODE_NAME)
  - L"VendorKeys" (EFI_VENDOR_KEYS_VARIABLE_NAME)

  The following UEFI variables are edk2 extensions:
  - L"SecureBootEnable" (EFI_SECURE_BOOT_ENABLE_NAME)
  - L"CustomMode"       (EFI_CUSTOM_MODE_NAME)

  The L"SecureBootEnable" UEFI variable is permitted to be missing, in which
  case the corresponding field in the SETTINGS object will be zeroed out. The
  rest of the covered UEFI variables are required to exist; otherwise, the
  function will fail.

  @param[out] Settings  The SETTINGS object to fill.

  @retval EFI_SUCCESS  Settings has been populated.

  @return              Error codes propagated from the GetExact() function. The
                       contents of Settings are indeterminate.
**/
STATIC
EFI_STATUS
GetSettings (
  OUT SETTINGS  *Settings
  )
{
  EFI_STATUS  Status;

  Status = GetExact (
             EFI_SETUP_MODE_NAME,
             &gEfiGlobalVariableGuid,
             &Settings->SetupMode,
             sizeof Settings->SetupMode,
             FALSE
             );
  if (EFI_ERROR (Status)) {
    return Status;
  }

  Status = GetExact (
             EFI_SECURE_BOOT_MODE_NAME,
             &gEfiGlobalVariableGuid,
             &Settings->SecureBoot,
             sizeof Settings->SecureBoot,
             FALSE
             );
  if (EFI_ERROR (Status)) {
    return Status;
  }

  Status = GetExact (
             EFI_SECURE_BOOT_ENABLE_NAME,
             &gEfiSecureBootEnableDisableGuid,
             &Settings->SecureBootEnable,
             sizeof Settings->SecureBootEnable,
             TRUE
             );
  if (EFI_ERROR (Status)) {
    return Status;
  }

  Status = GetExact (
             EFI_CUSTOM_MODE_NAME,
             &gEfiCustomModeEnableGuid,
             &Settings->CustomMode,
             sizeof Settings->CustomMode,
             FALSE
             );
  if (EFI_ERROR (Status)) {
    return Status;
  }

  Status = GetExact (
             EFI_VENDOR_KEYS_VARIABLE_NAME,
             &gEfiGlobalVariableGuid,
             &Settings->VendorKeys,
             sizeof Settings->VendorKeys,
             FALSE
             );
  return Status;
}

/**
  Print the contents of a SETTINGS structure to the UEFI console.

  @param[in] Settings  The SETTINGS object to print the contents of.
**/
STATIC
VOID
PrintSettings (
  IN CONST SETTINGS  *Settings
  )
{
  AsciiPrint (
    "info: SetupMode=%d SecureBoot=%d SecureBootEnable=%d "
    "CustomMode=%d VendorKeys=%d\n",
    Settings->SetupMode,
    Settings->SecureBoot,
    Settings->SecureBootEnable,
    Settings->CustomMode,
    Settings->VendorKeys
    );
}

/**
  Entry point function of this shell application.
**/
INTN
EFIAPI
ShellAppMain (
  IN UINTN   Argc,
  IN CHAR16  **Argv
  )
{
  INTN        RetVal;
  EFI_STATUS  Status;
  SETTINGS    Settings;
  UINT8       *PkKek1;
  UINTN       SizeOfPkKek1;
  BOOLEAN     NoDefault;

  if ((Argc == 2) && (StrCmp (Argv[1], L"--no-default") == 0)) {
    NoDefault = TRUE;
  } else {
    NoDefault = FALSE;
  }

  //
  // Prepare for failure.
  //
  RetVal = 1;

  //
  // If we're not in Setup Mode, we can't do anything.
  //
  Status = GetSettings (&Settings);
  if (EFI_ERROR (Status)) {
    return RetVal;
  }

  PrintSettings (&Settings);

  if (Settings.SetupMode != 1) {
    AsciiPrint ("error: already in User Mode\n");
    return RetVal;
  }

  //
  // Set PkKek1 and SizeOfPkKek1 to suppress incorrect compiler/analyzer
  // warnings.
  //
  PkKek1       = NULL;
  SizeOfPkKek1 = 0;

  //
  // Fetch the X509 certificate (to be used as Platform Key and first Key
  // Exchange Key) from SMBIOS.
  //
  Status = GetPkKek1 (&PkKek1, &SizeOfPkKek1);
  if (EFI_ERROR (Status)) {
    return RetVal;
  }

  //
  // Enter Custom Mode so we can enroll PK, KEK, db, and dbx without signature
  // checks on those variable writes.
  //
  if (Settings.CustomMode != CUSTOM_SECURE_BOOT_MODE) {
    Settings.CustomMode = CUSTOM_SECURE_BOOT_MODE;
    Status              = gRT->SetVariable (
                                 EFI_CUSTOM_MODE_NAME,
                                 &gEfiCustomModeEnableGuid,
                                 (EFI_VARIABLE_NON_VOLATILE |
                                  EFI_VARIABLE_BOOTSERVICE_ACCESS),
                                 sizeof Settings.CustomMode,
                                 &Settings.CustomMode
                                 );
    if (EFI_ERROR (Status)) {
      AsciiPrint (
        "error: SetVariable(\"%s\", %g): %r\n",
        EFI_CUSTOM_MODE_NAME,
        &gEfiCustomModeEnableGuid,
        Status
        );
      goto FreePkKek1;
    }
  }

  //
  // Enroll db.
  //
  if (NoDefault) {
    Status = EnrollListOfCerts (
               EFI_IMAGE_SECURITY_DATABASE,
               &gEfiImageSecurityDatabaseGuid,
               &gEfiCertX509Guid,
               PkKek1,
               SizeOfPkKek1,
               &gEfiCallerIdGuid,
               NULL
               );
  } else {
    Status = EnrollListOfCerts (
               EFI_IMAGE_SECURITY_DATABASE,
               &gEfiImageSecurityDatabaseGuid,
               &gEfiCertX509Guid,
               mMicrosoftPca,
               mSizeOfMicrosoftPca,
               &gMicrosoftVendorGuid,
               mMicrosoftUefiCa,
               mSizeOfMicrosoftUefiCa,
               &gMicrosoftVendorGuid,
               NULL
               );
  }

  if (EFI_ERROR (Status)) {
    goto FreePkKek1;
  }

  //
  // Enroll dbx.
  //
  Status = EnrollListOfCerts (
             EFI_IMAGE_SECURITY_DATABASE1,
             &gEfiImageSecurityDatabaseGuid,
             &gEfiCertSha256Guid,
             mSha256OfDevNull,
             mSizeOfSha256OfDevNull,
             &gEfiCallerIdGuid,
             NULL
             );
  if (EFI_ERROR (Status)) {
    goto FreePkKek1;
  }

  //
  // Enroll KEK.
  //
  if (NoDefault) {
    Status = EnrollListOfCerts (
               EFI_KEY_EXCHANGE_KEY_NAME,
               &gEfiGlobalVariableGuid,
               &gEfiCertX509Guid,
               PkKek1,
               SizeOfPkKek1,
               &gEfiCallerIdGuid,
               NULL
               );
  } else {
    Status = EnrollListOfCerts (
               EFI_KEY_EXCHANGE_KEY_NAME,
               &gEfiGlobalVariableGuid,
               &gEfiCertX509Guid,
               PkKek1,
               SizeOfPkKek1,
               &gEfiCallerIdGuid,
               mMicrosoftKek,
               mSizeOfMicrosoftKek,
               &gMicrosoftVendorGuid,
               NULL
               );
  }

  if (EFI_ERROR (Status)) {
    goto FreePkKek1;
  }

  //
  // Enroll PK, leaving Setup Mode (entering User Mode) at once.
  //
  Status = EnrollListOfCerts (
             EFI_PLATFORM_KEY_NAME,
             &gEfiGlobalVariableGuid,
             &gEfiCertX509Guid,
             PkKek1,
             SizeOfPkKek1,
             &gEfiGlobalVariableGuid,
             NULL
             );
  if (EFI_ERROR (Status)) {
    goto FreePkKek1;
  }

  //
  // Leave Custom Mode, so that updates to PK, KEK, db, and dbx require valid
  // signatures.
  //
  Settings.CustomMode = STANDARD_SECURE_BOOT_MODE;
  Status              = gRT->SetVariable (
                               EFI_CUSTOM_MODE_NAME,
                               &gEfiCustomModeEnableGuid,
                               EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS,
                               sizeof Settings.CustomMode,
                               &Settings.CustomMode
                               );
  if (EFI_ERROR (Status)) {
    AsciiPrint (
      "error: SetVariable(\"%s\", %g): %r\n",
      EFI_CUSTOM_MODE_NAME,
      &gEfiCustomModeEnableGuid,
      Status
      );
    goto FreePkKek1;
  }

  //
  // Final sanity check:
  //
  //                                 [SetupMode]
  //                        (read-only, standardized by UEFI)
  //                                /                \_
  //                               0               1, default
  //                              /                    \_
  //                      PK enrolled                   no PK enrolled yet,
  //              (this is called "User Mode")          PK enrollment possible
  //                             |
  //                             |
  //                     [SecureBootEnable]
  //         (read-write, edk2-specific, boot service only)
  //                /                           \_
  //               0                         1, default
  //              /                               \_
  //       [SecureBoot]=0                     [SecureBoot]=1
  // (read-only, standardized by UEFI)  (read-only, standardized by UEFI)
  //     images are not verified         images are verified, platform is
  //                                      operating in Secure Boot mode
  //                                                 |
  //                                                 |
  //                                           [CustomMode]
  //                          (read-write, edk2-specific, boot service only)
  //                                /                           \_
  //                          0, default                         1
  //                              /                               \_
  //                      PK, KEK, db, dbx                PK, KEK, db, dbx
  //                    updates are verified          updates are not verified
  //
  Status = GetSettings (&Settings);
  if (EFI_ERROR (Status)) {
    goto FreePkKek1;
  }

  PrintSettings (&Settings);

  if ((Settings.SetupMode != 0) || (Settings.SecureBoot != 1) ||
      (Settings.SecureBootEnable != 1) || (Settings.CustomMode != 0) ||
      (Settings.VendorKeys != 0))
  {
    AsciiPrint ("error: unexpected\n");
    goto FreePkKek1;
  }

  AsciiPrint ("info: success\n");
  RetVal = 0;

FreePkKek1:
  FreePool (PkKek1);

  return RetVal;
}
