Since commit 35e242b698 ("MdePkg/BaseLib: rewrite Base64Decode()",
2019-07-16), Base64Decode() guarantees that DestinationSize is larger on
output than it was on input if RETURN_BUFFER_TOO_SMALL is returned. Clean
up the retval handling for the first Base64Decode() call in
EnrollDefaultKeys, which used to work around the ambiguity in the previous
Base64Decode() interface contract.
Cc: Ard Biesheuvel <ard.biesheuvel@linaro.org>
Cc: Jordan Justen <jordan.l.justen@intel.com>
Cc: Philippe Mathieu-Daudé <philmd@redhat.com>
Ref: https://bugzilla.tianocore.org/show_bug.cgi?id=1981
Signed-off-by: Laszlo Ersek <lersek@redhat.com>
Reviewed-by: Philippe Mathieu-Daude <philmd@redhat.com>
Acked-by: Ard Biesheuvel <ard.biesheuvel@linaro.org>
		
	
		
			
				
	
	
		
			733 lines
		
	
	
		
			24 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			733 lines
		
	
	
		
			24 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /** @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", __FUNCTION__, 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;
 | |
| }
 |