2 Enroll default PK, KEK, db, dbx.
4 Copyright (C) 2014-2019, Red Hat, Inc.
6 SPDX-License-Identifier: BSD-2-Clause-Patent
8 #include <Guid/AuthenticatedVariableFormat.h> // gEfiCustomModeEnableGuid
9 #include <Guid/GlobalVariable.h> // EFI_SETUP_MODE_NAME
10 #include <Guid/ImageAuthentication.h> // EFI_IMAGE_SECURITY_DATABASE
11 #include <Guid/MicrosoftVendor.h> // gMicrosoftVendorGuid
12 #include <Guid/OvmfPkKek1AppPrefix.h> // gOvmfPkKek1AppPrefixGuid
13 #include <IndustryStandard/SmBios.h> // SMBIOS_HANDLE_PI_RESERVED
14 #include <Library/BaseLib.h> // GUID_STRING_LENGTH
15 #include <Library/BaseMemoryLib.h> // CopyGuid()
16 #include <Library/DebugLib.h> // ASSERT()
17 #include <Library/MemoryAllocationLib.h> // FreePool()
18 #include <Library/PrintLib.h> // AsciiSPrint()
19 #include <Library/ShellCEntryLib.h> // ShellAppMain()
20 #include <Library/UefiBootServicesTableLib.h> // gBS
21 #include <Library/UefiLib.h> // AsciiPrint()
22 #include <Library/UefiRuntimeServicesTableLib.h> // gRT
23 #include <Protocol/Smbios.h> // EFI_SMBIOS_PROTOCOL
25 #include "EnrollDefaultKeys.h"
29 Fetch the X509 certificate (to be used as Platform Key and first Key Exchange
32 @param[out] PkKek1 The X509 certificate in DER encoding from the
33 hypervisor, to be enrolled as PK and first KEK
34 entry. On success, the caller is responsible for
35 releasing PkKek1 with FreePool().
37 @param[out] SizeOfPkKek1 The size of PkKek1 in bytes.
39 @retval EFI_SUCCESS PkKek1 and SizeOfPkKek1 have been set
42 @retval EFI_NOT_FOUND An OEM String matching
43 OVMF_PK_KEK1_APP_PREFIX_GUID has not been
46 @retval EFI_PROTOCOL_ERROR In the OEM String matching
47 OVMF_PK_KEK1_APP_PREFIX_GUID, the certificate
48 is empty, or it has invalid base64 encoding.
50 @retval EFI_OUT_OF_RESOURCES Memory allocation failed.
52 @return Error codes from gBS->LocateProtocol().
58 OUT UINTN
*SizeOfPkKek1
61 CONST CHAR8
*Base64Cert
;
62 CHAR8 OvmfPkKek1AppPrefix
[GUID_STRING_LENGTH
+ 1 + 1];
64 EFI_SMBIOS_PROTOCOL
*Smbios
;
65 EFI_SMBIOS_HANDLE Handle
;
67 EFI_SMBIOS_TABLE_HEADER
*Header
;
68 SMBIOS_TABLE_TYPE11
*OemStringsTable
;
70 UINTN DecodedCertSize
;
76 // Format the application prefix, for OEM String matching.
78 AsciiSPrint (OvmfPkKek1AppPrefix
, sizeof OvmfPkKek1AppPrefix
, "%g:",
79 &gOvmfPkKek1AppPrefixGuid
);
82 // Scan all "OEM Strings" tables.
84 Status
= gBS
->LocateProtocol (&gEfiSmbiosProtocolGuid
, NULL
,
86 if (EFI_ERROR (Status
)) {
87 AsciiPrint ("error: failed to locate EFI_SMBIOS_PROTOCOL: %r\n", Status
);
91 Handle
= SMBIOS_HANDLE_PI_RESERVED
;
92 Type
= SMBIOS_TYPE_OEM_STRINGS
;
93 for (Status
= Smbios
->GetNext (Smbios
, &Handle
, &Type
, &Header
, NULL
);
95 Status
= Smbios
->GetNext (Smbios
, &Handle
, &Type
, &Header
, NULL
)) {
96 CONST CHAR8
*OemString
;
99 if (Header
->Length
< sizeof *OemStringsTable
) {
101 // Malformed table header, skip to next.
105 OemStringsTable
= (SMBIOS_TABLE_TYPE11
*)Header
;
108 // Scan all strings in the unformatted area of the current "OEM Strings"
111 OemString
= (CONST CHAR8
*)(OemStringsTable
+ 1);
112 for (Idx
= 0; Idx
< OemStringsTable
->StringCount
; ++Idx
) {
113 CHAR8 CandidatePrefix
[sizeof OvmfPkKek1AppPrefix
];
116 // NUL-terminate the candidate prefix for case-insensitive comparison.
118 AsciiStrnCpyS (CandidatePrefix
, sizeof CandidatePrefix
, OemString
,
119 GUID_STRING_LENGTH
+ 1);
120 if (AsciiStriCmp (OvmfPkKek1AppPrefix
, CandidatePrefix
) == 0) {
122 // The current string matches the prefix.
124 Base64Cert
= OemString
+ GUID_STRING_LENGTH
+ 1;
127 OemString
+= AsciiStrSize (OemString
);
130 if (Idx
< OemStringsTable
->StringCount
) {
132 // The current table has a matching string.
138 if (EFI_ERROR (Status
)) {
140 // No table with a matching string has been found.
142 AsciiPrint ("error: OEM String with app prefix %g not found: %r\n",
143 &gOvmfPkKek1AppPrefixGuid
, Status
);
144 return EFI_NOT_FOUND
;
147 ASSERT (Base64Cert
!= NULL
);
148 Base64CertLen
= AsciiStrLen (Base64Cert
);
151 // Verify the base64 encoding, and determine the decoded size.
154 Status
= Base64Decode (Base64Cert
, Base64CertLen
, NULL
, &DecodedCertSize
);
156 case EFI_BUFFER_TOO_SMALL
:
157 if (DecodedCertSize
> 0) {
161 // Fall through: the above Base64Decode() call is ill-specified in BaseLib
162 // if Source decodes to zero bytes (for example if it consists of ignored
166 AsciiPrint ("error: empty certificate after app prefix %g\n",
167 &gOvmfPkKek1AppPrefixGuid
);
168 return EFI_PROTOCOL_ERROR
;
170 AsciiPrint ("error: invalid base64 string after app prefix %g\n",
171 &gOvmfPkKek1AppPrefixGuid
);
172 return EFI_PROTOCOL_ERROR
;
176 // Allocate the output buffer.
178 DecodedCert
= AllocatePool (DecodedCertSize
);
179 if (DecodedCert
== NULL
) {
180 AsciiPrint ("error: failed to allocate memory\n");
181 return EFI_OUT_OF_RESOURCES
;
185 // Decoding will succeed at this point.
187 Status
= Base64Decode (Base64Cert
, Base64CertLen
, DecodedCert
,
189 ASSERT_EFI_ERROR (Status
);
191 *PkKek1
= DecodedCert
;
192 *SizeOfPkKek1
= DecodedCertSize
;
198 Enroll a set of certificates in a global variable, overwriting it.
200 The variable will be rewritten with NV+BS+RT+AT attributes.
202 @param[in] VariableName The name of the variable to overwrite.
204 @param[in] VendorGuid The namespace (ie. vendor GUID) of the variable to
207 @param[in] CertType The GUID determining the type of all the
208 certificates in the set that is passed in. For
209 example, gEfiCertX509Guid stands for DER-encoded
210 X.509 certificates, while gEfiCertSha256Guid stands
211 for SHA256 image hashes.
213 @param[in] ... A list of
215 IN CONST UINT8 *Cert,
217 IN CONST EFI_GUID *OwnerGuid
219 triplets. If the first component of a triplet is
220 NULL, then the other two components are not
221 accessed, and processing is terminated. The list of
222 certificates is enrolled in the variable specified,
223 overwriting it. The OwnerGuid component identifies
224 the agent installing the certificate.
226 @retval EFI_INVALID_PARAMETER The triplet list is empty (ie. the first Cert
227 value is NULL), or one of the CertSize values
228 is 0, or one of the CertSize values would
229 overflow the accumulated UINT32 data size.
231 @retval EFI_OUT_OF_RESOURCES Out of memory while formatting variable
234 @retval EFI_SUCCESS Enrollment successful; the variable has been
235 overwritten (or created).
237 @return Error codes from gRT->GetTime() and
244 IN CHAR16
*VariableName
,
245 IN EFI_GUID
*VendorGuid
,
246 IN EFI_GUID
*CertType
,
251 SINGLE_HEADER
*SingleHeader
;
252 REPEATING_HEADER
*RepeatingHeader
;
259 Status
= EFI_SUCCESS
;
262 // compute total size first, for UINT32 range check, and allocation
264 DataSize
= sizeof *SingleHeader
;
265 VA_START (Marker
, CertType
);
266 for (Cert
= VA_ARG (Marker
, CONST UINT8
*);
268 Cert
= VA_ARG (Marker
, CONST UINT8
*)) {
271 CertSize
= VA_ARG (Marker
, UINTN
);
272 (VOID
)VA_ARG (Marker
, CONST EFI_GUID
*);
275 CertSize
> MAX_UINT32
- sizeof *RepeatingHeader
||
276 DataSize
> MAX_UINT32
- sizeof *RepeatingHeader
- CertSize
) {
277 Status
= EFI_INVALID_PARAMETER
;
280 DataSize
+= sizeof *RepeatingHeader
+ CertSize
;
284 if (DataSize
== sizeof *SingleHeader
) {
285 Status
= EFI_INVALID_PARAMETER
;
287 if (EFI_ERROR (Status
)) {
291 Data
= AllocatePool (DataSize
);
293 Status
= EFI_OUT_OF_RESOURCES
;
299 SingleHeader
= (SINGLE_HEADER
*)Position
;
300 Status
= gRT
->GetTime (&SingleHeader
->TimeStamp
, NULL
);
301 if (EFI_ERROR (Status
)) {
304 SingleHeader
->TimeStamp
.Pad1
= 0;
305 SingleHeader
->TimeStamp
.Nanosecond
= 0;
306 SingleHeader
->TimeStamp
.TimeZone
= 0;
307 SingleHeader
->TimeStamp
.Daylight
= 0;
308 SingleHeader
->TimeStamp
.Pad2
= 0;
310 SingleHeader
->dwLength
= DataSize
- sizeof SingleHeader
->TimeStamp
;
313 // This looks like a bug in edk2. According to the UEFI specification,
314 // dwLength is "The length of the entire certificate, including the length of
315 // the header, in bytes". That shouldn't stop right after CertType -- it
316 // should include everything below it.
318 SingleHeader
->dwLength
= sizeof *SingleHeader
319 - sizeof SingleHeader
->TimeStamp
;
321 SingleHeader
->wRevision
= 0x0200;
322 SingleHeader
->wCertificateType
= WIN_CERT_TYPE_EFI_GUID
;
323 CopyGuid (&SingleHeader
->CertType
, &gEfiCertPkcs7Guid
);
324 Position
+= sizeof *SingleHeader
;
326 VA_START (Marker
, CertType
);
327 for (Cert
= VA_ARG (Marker
, CONST UINT8
*);
329 Cert
= VA_ARG (Marker
, CONST UINT8
*)) {
331 CONST EFI_GUID
*OwnerGuid
;
333 CertSize
= VA_ARG (Marker
, UINTN
);
334 OwnerGuid
= VA_ARG (Marker
, CONST EFI_GUID
*);
336 RepeatingHeader
= (REPEATING_HEADER
*)Position
;
337 CopyGuid (&RepeatingHeader
->SignatureType
, CertType
);
338 RepeatingHeader
->SignatureListSize
=
339 (UINT32
)(sizeof *RepeatingHeader
+ CertSize
);
340 RepeatingHeader
->SignatureHeaderSize
= 0;
341 RepeatingHeader
->SignatureSize
=
342 (UINT32
)(sizeof RepeatingHeader
->SignatureOwner
+ CertSize
);
343 CopyGuid (&RepeatingHeader
->SignatureOwner
, OwnerGuid
);
344 Position
+= sizeof *RepeatingHeader
;
346 CopyMem (Position
, Cert
, CertSize
);
347 Position
+= CertSize
;
351 ASSERT (Data
+ DataSize
== Position
);
353 Status
= gRT
->SetVariable (VariableName
, VendorGuid
,
354 (EFI_VARIABLE_NON_VOLATILE
|
355 EFI_VARIABLE_BOOTSERVICE_ACCESS
|
356 EFI_VARIABLE_RUNTIME_ACCESS
|
357 EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS
),
364 if (EFI_ERROR (Status
)) {
365 AsciiPrint ("error: %a(\"%s\", %g): %r\n", __FUNCTION__
, VariableName
,
373 Read a UEFI variable into a caller-allocated buffer, enforcing an exact size.
375 @param[in] VariableName The name of the variable to read; passed to
378 @param[in] VendorGuid The vendor (namespace) GUID of the variable to read;
379 passed to gRT->GetVariable().
381 @param[out] Data The caller-allocated buffer that is supposed to
382 receive the variable's contents. On error, the
383 contents of Data are indeterminate.
385 @param[in] DataSize The size in bytes that the caller requires the UEFI
386 variable to have. The caller is responsible for
387 providing room for DataSize bytes in Data.
389 @param[in] AllowMissing If FALSE, the variable is required to exist. If
390 TRUE, the variable is permitted to be missing.
392 @retval EFI_SUCCESS The UEFI variable exists, has the required size
393 (DataSize), and has been read into Data.
395 @retval EFI_SUCCESS The UEFI variable doesn't exist, and
396 AllowMissing is TRUE. DataSize bytes in Data
397 have been zeroed out.
399 @retval EFI_NOT_FOUND The UEFI variable doesn't exist, and
400 AllowMissing is FALSE.
402 @retval EFI_BUFFER_TOO_SMALL The UEFI variable exists, but its size is
403 greater than DataSize.
405 @retval EFI_PROTOCOL_ERROR The UEFI variable exists, but its size is
406 smaller than DataSize.
408 @return Error codes propagated from gRT->GetVariable().
413 IN CHAR16
*VariableName
,
414 IN EFI_GUID
*VendorGuid
,
417 IN BOOLEAN AllowMissing
424 Status
= gRT
->GetVariable (VariableName
, VendorGuid
, NULL
, &Size
, Data
);
425 if (EFI_ERROR (Status
)) {
426 if (Status
== EFI_NOT_FOUND
&& AllowMissing
) {
427 ZeroMem (Data
, DataSize
);
431 AsciiPrint ("error: GetVariable(\"%s\", %g): %r\n", VariableName
,
436 if (Size
!= DataSize
) {
437 AsciiPrint ("error: GetVariable(\"%s\", %g): expected size 0x%Lx, "
438 "got 0x%Lx\n", VariableName
, VendorGuid
, (UINT64
)DataSize
, (UINT64
)Size
);
439 return EFI_PROTOCOL_ERROR
;
447 Populate a SETTINGS structure from the underlying UEFI variables.
449 The following UEFI variables are standard variables:
450 - L"SetupMode" (EFI_SETUP_MODE_NAME)
451 - L"SecureBoot" (EFI_SECURE_BOOT_MODE_NAME)
452 - L"VendorKeys" (EFI_VENDOR_KEYS_VARIABLE_NAME)
454 The following UEFI variables are edk2 extensions:
455 - L"SecureBootEnable" (EFI_SECURE_BOOT_ENABLE_NAME)
456 - L"CustomMode" (EFI_CUSTOM_MODE_NAME)
458 The L"SecureBootEnable" UEFI variable is permitted to be missing, in which
459 case the corresponding field in the SETTINGS object will be zeroed out. The
460 rest of the covered UEFI variables are required to exist; otherwise, the
463 @param[out] Settings The SETTINGS object to fill.
465 @retval EFI_SUCCESS Settings has been populated.
467 @return Error codes propagated from the GetExact() function. The
468 contents of Settings are indeterminate.
473 OUT SETTINGS
*Settings
478 Status
= GetExact (EFI_SETUP_MODE_NAME
, &gEfiGlobalVariableGuid
,
479 &Settings
->SetupMode
, sizeof Settings
->SetupMode
, FALSE
);
480 if (EFI_ERROR (Status
)) {
484 Status
= GetExact (EFI_SECURE_BOOT_MODE_NAME
, &gEfiGlobalVariableGuid
,
485 &Settings
->SecureBoot
, sizeof Settings
->SecureBoot
, FALSE
);
486 if (EFI_ERROR (Status
)) {
490 Status
= GetExact (EFI_SECURE_BOOT_ENABLE_NAME
,
491 &gEfiSecureBootEnableDisableGuid
, &Settings
->SecureBootEnable
,
492 sizeof Settings
->SecureBootEnable
, TRUE
);
493 if (EFI_ERROR (Status
)) {
497 Status
= GetExact (EFI_CUSTOM_MODE_NAME
, &gEfiCustomModeEnableGuid
,
498 &Settings
->CustomMode
, sizeof Settings
->CustomMode
, FALSE
);
499 if (EFI_ERROR (Status
)) {
503 Status
= GetExact (EFI_VENDOR_KEYS_VARIABLE_NAME
, &gEfiGlobalVariableGuid
,
504 &Settings
->VendorKeys
, sizeof Settings
->VendorKeys
, FALSE
);
510 Print the contents of a SETTINGS structure to the UEFI console.
512 @param[in] Settings The SETTINGS object to print the contents of.
517 IN CONST SETTINGS
*Settings
520 AsciiPrint ("info: SetupMode=%d SecureBoot=%d SecureBootEnable=%d "
521 "CustomMode=%d VendorKeys=%d\n", Settings
->SetupMode
, Settings
->SecureBoot
,
522 Settings
->SecureBootEnable
, Settings
->CustomMode
, Settings
->VendorKeys
);
527 Entry point function of this shell application.
543 // Prepare for failure.
548 // If we're not in Setup Mode, we can't do anything.
550 Status
= GetSettings (&Settings
);
551 if (EFI_ERROR (Status
)) {
554 PrintSettings (&Settings
);
556 if (Settings
.SetupMode
!= 1) {
557 AsciiPrint ("error: already in User Mode\n");
562 // Fetch the X509 certificate (to be used as Platform Key and first Key
563 // Exchange Key) from SMBIOS.
565 Status
= GetPkKek1 (&PkKek1
, &SizeOfPkKek1
);
566 if (EFI_ERROR (Status
)) {
571 // Enter Custom Mode so we can enroll PK, KEK, db, and dbx without signature
572 // checks on those variable writes.
574 if (Settings
.CustomMode
!= CUSTOM_SECURE_BOOT_MODE
) {
575 Settings
.CustomMode
= CUSTOM_SECURE_BOOT_MODE
;
576 Status
= gRT
->SetVariable (EFI_CUSTOM_MODE_NAME
, &gEfiCustomModeEnableGuid
,
577 (EFI_VARIABLE_NON_VOLATILE
|
578 EFI_VARIABLE_BOOTSERVICE_ACCESS
),
579 sizeof Settings
.CustomMode
, &Settings
.CustomMode
);
580 if (EFI_ERROR (Status
)) {
581 AsciiPrint ("error: SetVariable(\"%s\", %g): %r\n", EFI_CUSTOM_MODE_NAME
,
582 &gEfiCustomModeEnableGuid
, Status
);
590 Status
= EnrollListOfCerts (
591 EFI_IMAGE_SECURITY_DATABASE
,
592 &gEfiImageSecurityDatabaseGuid
,
594 mMicrosoftPca
, mSizeOfMicrosoftPca
, &gMicrosoftVendorGuid
,
595 mMicrosoftUefiCa
, mSizeOfMicrosoftUefiCa
, &gMicrosoftVendorGuid
,
597 if (EFI_ERROR (Status
)) {
604 Status
= EnrollListOfCerts (
605 EFI_IMAGE_SECURITY_DATABASE1
,
606 &gEfiImageSecurityDatabaseGuid
,
608 mSha256OfDevNull
, mSizeOfSha256OfDevNull
, &gEfiCallerIdGuid
,
610 if (EFI_ERROR (Status
)) {
617 Status
= EnrollListOfCerts (
618 EFI_KEY_EXCHANGE_KEY_NAME
,
619 &gEfiGlobalVariableGuid
,
621 PkKek1
, SizeOfPkKek1
, &gEfiCallerIdGuid
,
622 mMicrosoftKek
, mSizeOfMicrosoftKek
, &gMicrosoftVendorGuid
,
624 if (EFI_ERROR (Status
)) {
629 // Enroll PK, leaving Setup Mode (entering User Mode) at once.
631 Status
= EnrollListOfCerts (
632 EFI_PLATFORM_KEY_NAME
,
633 &gEfiGlobalVariableGuid
,
635 PkKek1
, SizeOfPkKek1
, &gEfiGlobalVariableGuid
,
637 if (EFI_ERROR (Status
)) {
642 // Leave Custom Mode, so that updates to PK, KEK, db, and dbx require valid
645 Settings
.CustomMode
= STANDARD_SECURE_BOOT_MODE
;
646 Status
= gRT
->SetVariable (EFI_CUSTOM_MODE_NAME
, &gEfiCustomModeEnableGuid
,
647 EFI_VARIABLE_NON_VOLATILE
| EFI_VARIABLE_BOOTSERVICE_ACCESS
,
648 sizeof Settings
.CustomMode
, &Settings
.CustomMode
);
649 if (EFI_ERROR (Status
)) {
650 AsciiPrint ("error: SetVariable(\"%s\", %g): %r\n", EFI_CUSTOM_MODE_NAME
,
651 &gEfiCustomModeEnableGuid
, Status
);
656 // Final sanity check:
659 // (read-only, standardized by UEFI)
663 // PK enrolled no PK enrolled yet,
664 // (this is called "User Mode") PK enrollment possible
667 // [SecureBootEnable]
668 // (read-write, edk2-specific, boot service only)
672 // [SecureBoot]=0 [SecureBoot]=1
673 // (read-only, standardized by UEFI) (read-only, standardized by UEFI)
674 // images are not verified images are verified, platform is
675 // operating in Secure Boot mode
679 // (read-write, edk2-specific, boot service only)
683 // PK, KEK, db, dbx PK, KEK, db, dbx
684 // updates are verified updates are not verified
686 Status
= GetSettings (&Settings
);
687 if (EFI_ERROR (Status
)) {
690 PrintSettings (&Settings
);
692 if (Settings
.SetupMode
!= 0 || Settings
.SecureBoot
!= 1 ||
693 Settings
.SecureBootEnable
!= 1 || Settings
.CustomMode
!= 0 ||
694 Settings
.VendorKeys
!= 0) {
695 AsciiPrint ("error: unexpected\n");
699 AsciiPrint ("info: success\n");