2019-04-25 13:15:13 +02:00
|
|
|
/** @file
|
2019-04-25 13:33:44 +02:00
|
|
|
Enroll default PK, KEK, db, dbx.
|
2019-04-25 13:15:13 +02:00
|
|
|
|
2019-04-25 13:33:44 +02:00
|
|
|
Copyright (C) 2014-2019, Red Hat, Inc.
|
2019-04-25 13:15:13 +02:00
|
|
|
|
2019-04-25 13:33:44 +02:00
|
|
|
SPDX-License-Identifier: BSD-2-Clause-Patent
|
2019-04-25 13:15:13 +02:00
|
|
|
**/
|
|
|
|
#include <Guid/AuthenticatedVariableFormat.h> // gEfiCustomModeEnableGuid
|
|
|
|
#include <Guid/GlobalVariable.h> // EFI_SETUP_MODE_NAME
|
|
|
|
#include <Guid/ImageAuthentication.h> // EFI_IMAGE_SECURITY_DATABASE
|
2019-04-25 21:49:38 +02:00
|
|
|
#include <Guid/MicrosoftVendor.h> // gMicrosoftVendorGuid
|
2019-04-25 23:15:14 +02:00
|
|
|
#include <Guid/OvmfPkKek1AppPrefix.h> // gOvmfPkKek1AppPrefixGuid
|
|
|
|
#include <IndustryStandard/SmBios.h> // SMBIOS_HANDLE_PI_RESERVED
|
|
|
|
#include <Library/BaseLib.h> // GUID_STRING_LENGTH
|
2019-04-25 13:15:13 +02:00
|
|
|
#include <Library/BaseMemoryLib.h> // CopyGuid()
|
|
|
|
#include <Library/DebugLib.h> // ASSERT()
|
|
|
|
#include <Library/MemoryAllocationLib.h> // FreePool()
|
2019-04-25 23:15:14 +02:00
|
|
|
#include <Library/PrintLib.h> // AsciiSPrint()
|
2019-04-25 13:15:13 +02:00
|
|
|
#include <Library/ShellCEntryLib.h> // ShellAppMain()
|
2019-04-25 23:15:14 +02:00
|
|
|
#include <Library/UefiBootServicesTableLib.h> // gBS
|
2019-04-25 13:15:13 +02:00
|
|
|
#include <Library/UefiLib.h> // AsciiPrint()
|
|
|
|
#include <Library/UefiRuntimeServicesTableLib.h> // gRT
|
2019-04-25 23:15:14 +02:00
|
|
|
#include <Protocol/Smbios.h> // EFI_SMBIOS_PROTOCOL
|
2019-04-25 13:15:13 +02:00
|
|
|
|
2019-04-25 15:27:57 +02:00
|
|
|
#include "EnrollDefaultKeys.h"
|
|
|
|
|
2019-04-25 17:03:15 +02:00
|
|
|
|
2019-04-25 23:15:14 +02:00
|
|
|
/**
|
|
|
|
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:
|
|
|
|
if (DecodedCertSize > 0) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
//
|
|
|
|
// Fall through: the above Base64Decode() call is ill-specified in BaseLib
|
|
|
|
// if Source decodes to zero bytes (for example if it consists of ignored
|
|
|
|
// whitespace only).
|
|
|
|
//
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2019-04-25 13:15:13 +02:00
|
|
|
/**
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2019-04-25 17:03:15 +02:00
|
|
|
/**
|
|
|
|
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().
|
|
|
|
**/
|
2019-04-25 13:15:13 +02:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2019-04-25 17:03:15 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
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.
|
|
|
|
**/
|
2019-04-25 13:15:13 +02:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2019-04-25 17:03:15 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
Print the contents of a SETTINGS structure to the UEFI console.
|
|
|
|
|
|
|
|
@param[in] Settings The SETTINGS object to print the contents of.
|
|
|
|
**/
|
2019-04-25 13:15:13 +02:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2019-04-25 17:03:15 +02:00
|
|
|
/**
|
|
|
|
Entry point function of this shell application.
|
|
|
|
**/
|
2019-04-25 13:15:13 +02:00
|
|
|
INTN
|
|
|
|
EFIAPI
|
|
|
|
ShellAppMain (
|
|
|
|
IN UINTN Argc,
|
|
|
|
IN CHAR16 **Argv
|
|
|
|
)
|
|
|
|
{
|
2019-04-25 23:15:14 +02:00
|
|
|
INTN RetVal;
|
2019-04-25 13:15:13 +02:00
|
|
|
EFI_STATUS Status;
|
|
|
|
SETTINGS Settings;
|
2019-04-25 23:15:14 +02:00
|
|
|
UINT8 *PkKek1;
|
|
|
|
UINTN SizeOfPkKek1;
|
2019-05-16 05:08:34 +02:00
|
|
|
BOOLEAN NoDefault;
|
|
|
|
|
|
|
|
if (Argc == 2 && StrCmp (Argv[1], L"--no-default") == 0) {
|
|
|
|
NoDefault = TRUE;
|
|
|
|
} else {
|
|
|
|
NoDefault = FALSE;
|
|
|
|
}
|
2019-04-25 23:15:14 +02:00
|
|
|
|
|
|
|
//
|
|
|
|
// Prepare for failure.
|
|
|
|
//
|
|
|
|
RetVal = 1;
|
2019-04-25 13:15:13 +02:00
|
|
|
|
2019-04-25 22:19:36 +02:00
|
|
|
//
|
|
|
|
// If we're not in Setup Mode, we can't do anything.
|
|
|
|
//
|
2019-04-25 13:15:13 +02:00
|
|
|
Status = GetSettings (&Settings);
|
|
|
|
if (EFI_ERROR (Status)) {
|
2019-04-25 23:15:14 +02:00
|
|
|
return RetVal;
|
2019-04-25 13:15:13 +02:00
|
|
|
}
|
|
|
|
PrintSettings (&Settings);
|
|
|
|
|
|
|
|
if (Settings.SetupMode != 1) {
|
|
|
|
AsciiPrint ("error: already in User Mode\n");
|
2019-04-25 23:15:14 +02:00
|
|
|
return RetVal;
|
|
|
|
}
|
|
|
|
|
2019-04-30 23:33:39 +02:00
|
|
|
//
|
|
|
|
// Set PkKek1 and SizeOfPkKek1 to suppress incorrect compiler/analyzer
|
|
|
|
// warnings.
|
|
|
|
//
|
|
|
|
PkKek1 = NULL;
|
|
|
|
SizeOfPkKek1 = 0;
|
|
|
|
|
2019-04-25 23:15:14 +02:00
|
|
|
//
|
|
|
|
// 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;
|
2019-04-25 13:15:13 +02:00
|
|
|
}
|
|
|
|
|
2019-04-25 22:19:36 +02:00
|
|
|
//
|
|
|
|
// Enter Custom Mode so we can enroll PK, KEK, db, and dbx without signature
|
|
|
|
// checks on those variable writes.
|
|
|
|
//
|
2019-04-25 13:15:13 +02:00
|
|
|
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);
|
2019-04-25 23:15:14 +02:00
|
|
|
goto FreePkKek1;
|
2019-04-25 13:15:13 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-04-25 22:19:36 +02:00
|
|
|
//
|
|
|
|
// Enroll db.
|
|
|
|
//
|
2019-05-16 05:08:34 +02:00
|
|
|
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);
|
|
|
|
}
|
2019-04-25 13:15:13 +02:00
|
|
|
if (EFI_ERROR (Status)) {
|
2019-04-25 23:15:14 +02:00
|
|
|
goto FreePkKek1;
|
2019-04-25 13:15:13 +02:00
|
|
|
}
|
|
|
|
|
2019-04-25 22:19:36 +02:00
|
|
|
//
|
|
|
|
// Enroll dbx.
|
|
|
|
//
|
2019-04-25 13:15:13 +02:00
|
|
|
Status = EnrollListOfCerts (
|
|
|
|
EFI_IMAGE_SECURITY_DATABASE1,
|
|
|
|
&gEfiImageSecurityDatabaseGuid,
|
|
|
|
&gEfiCertSha256Guid,
|
2019-04-25 16:03:05 +02:00
|
|
|
mSha256OfDevNull, mSizeOfSha256OfDevNull, &gEfiCallerIdGuid,
|
2019-04-25 13:15:13 +02:00
|
|
|
NULL);
|
|
|
|
if (EFI_ERROR (Status)) {
|
2019-04-25 23:15:14 +02:00
|
|
|
goto FreePkKek1;
|
2019-04-25 13:15:13 +02:00
|
|
|
}
|
|
|
|
|
2019-04-25 22:19:36 +02:00
|
|
|
//
|
|
|
|
// Enroll KEK.
|
|
|
|
//
|
2019-05-16 05:08:34 +02:00
|
|
|
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);
|
|
|
|
}
|
2019-04-25 13:15:13 +02:00
|
|
|
if (EFI_ERROR (Status)) {
|
2019-04-25 23:15:14 +02:00
|
|
|
goto FreePkKek1;
|
2019-04-25 13:15:13 +02:00
|
|
|
}
|
|
|
|
|
2019-04-25 22:19:36 +02:00
|
|
|
//
|
|
|
|
// Enroll PK, leaving Setup Mode (entering User Mode) at once.
|
|
|
|
//
|
2019-04-25 13:15:13 +02:00
|
|
|
Status = EnrollListOfCerts (
|
|
|
|
EFI_PLATFORM_KEY_NAME,
|
|
|
|
&gEfiGlobalVariableGuid,
|
|
|
|
&gEfiCertX509Guid,
|
2019-04-25 23:15:14 +02:00
|
|
|
PkKek1, SizeOfPkKek1, &gEfiGlobalVariableGuid,
|
2019-04-25 13:15:13 +02:00
|
|
|
NULL);
|
|
|
|
if (EFI_ERROR (Status)) {
|
2019-04-25 23:15:14 +02:00
|
|
|
goto FreePkKek1;
|
2019-04-25 13:15:13 +02:00
|
|
|
}
|
|
|
|
|
2019-04-25 22:19:36 +02:00
|
|
|
//
|
|
|
|
// Leave Custom Mode, so that updates to PK, KEK, db, and dbx require valid
|
|
|
|
// signatures.
|
|
|
|
//
|
2019-04-25 13:15:13 +02:00
|
|
|
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);
|
2019-04-25 23:15:14 +02:00
|
|
|
goto FreePkKek1;
|
2019-04-25 13:15:13 +02:00
|
|
|
}
|
|
|
|
|
2019-04-25 22:19:36 +02:00
|
|
|
//
|
|
|
|
// 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
|
|
|
|
//
|
2019-04-25 13:15:13 +02:00
|
|
|
Status = GetSettings (&Settings);
|
|
|
|
if (EFI_ERROR (Status)) {
|
2019-04-25 23:15:14 +02:00
|
|
|
goto FreePkKek1;
|
2019-04-25 13:15:13 +02:00
|
|
|
}
|
|
|
|
PrintSettings (&Settings);
|
|
|
|
|
|
|
|
if (Settings.SetupMode != 0 || Settings.SecureBoot != 1 ||
|
|
|
|
Settings.SecureBootEnable != 1 || Settings.CustomMode != 0 ||
|
|
|
|
Settings.VendorKeys != 0) {
|
|
|
|
AsciiPrint ("error: unexpected\n");
|
2019-04-25 23:15:14 +02:00
|
|
|
goto FreePkKek1;
|
2019-04-25 13:15:13 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
AsciiPrint ("info: success\n");
|
2019-04-25 23:15:14 +02:00
|
|
|
RetVal = 0;
|
|
|
|
|
|
|
|
FreePkKek1:
|
|
|
|
FreePool (PkKek1);
|
|
|
|
|
|
|
|
return RetVal;
|
2019-04-25 13:15:13 +02:00
|
|
|
}
|