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 ASSERT (DecodedCertSize
> 0);
160 AsciiPrint ("error: empty certificate after app prefix %g\n",
161 &gOvmfPkKek1AppPrefixGuid
);
162 return EFI_PROTOCOL_ERROR
;
164 AsciiPrint ("error: invalid base64 string after app prefix %g\n",
165 &gOvmfPkKek1AppPrefixGuid
);
166 return EFI_PROTOCOL_ERROR
;
170 // Allocate the output buffer.
172 DecodedCert
= AllocatePool (DecodedCertSize
);
173 if (DecodedCert
== NULL
) {
174 AsciiPrint ("error: failed to allocate memory\n");
175 return EFI_OUT_OF_RESOURCES
;
179 // Decoding will succeed at this point.
181 Status
= Base64Decode (Base64Cert
, Base64CertLen
, DecodedCert
,
183 ASSERT_EFI_ERROR (Status
);
185 *PkKek1
= DecodedCert
;
186 *SizeOfPkKek1
= DecodedCertSize
;
192 Enroll a set of certificates in a global variable, overwriting it.
194 The variable will be rewritten with NV+BS+RT+AT attributes.
196 @param[in] VariableName The name of the variable to overwrite.
198 @param[in] VendorGuid The namespace (ie. vendor GUID) of the variable to
201 @param[in] CertType The GUID determining the type of all the
202 certificates in the set that is passed in. For
203 example, gEfiCertX509Guid stands for DER-encoded
204 X.509 certificates, while gEfiCertSha256Guid stands
205 for SHA256 image hashes.
207 @param[in] ... A list of
209 IN CONST UINT8 *Cert,
211 IN CONST EFI_GUID *OwnerGuid
213 triplets. If the first component of a triplet is
214 NULL, then the other two components are not
215 accessed, and processing is terminated. The list of
216 certificates is enrolled in the variable specified,
217 overwriting it. The OwnerGuid component identifies
218 the agent installing the certificate.
220 @retval EFI_INVALID_PARAMETER The triplet list is empty (ie. the first Cert
221 value is NULL), or one of the CertSize values
222 is 0, or one of the CertSize values would
223 overflow the accumulated UINT32 data size.
225 @retval EFI_OUT_OF_RESOURCES Out of memory while formatting variable
228 @retval EFI_SUCCESS Enrollment successful; the variable has been
229 overwritten (or created).
231 @return Error codes from gRT->GetTime() and
238 IN CHAR16
*VariableName
,
239 IN EFI_GUID
*VendorGuid
,
240 IN EFI_GUID
*CertType
,
245 SINGLE_HEADER
*SingleHeader
;
246 REPEATING_HEADER
*RepeatingHeader
;
253 Status
= EFI_SUCCESS
;
256 // compute total size first, for UINT32 range check, and allocation
258 DataSize
= sizeof *SingleHeader
;
259 VA_START (Marker
, CertType
);
260 for (Cert
= VA_ARG (Marker
, CONST UINT8
*);
262 Cert
= VA_ARG (Marker
, CONST UINT8
*)) {
265 CertSize
= VA_ARG (Marker
, UINTN
);
266 (VOID
)VA_ARG (Marker
, CONST EFI_GUID
*);
269 CertSize
> MAX_UINT32
- sizeof *RepeatingHeader
||
270 DataSize
> MAX_UINT32
- sizeof *RepeatingHeader
- CertSize
) {
271 Status
= EFI_INVALID_PARAMETER
;
274 DataSize
+= sizeof *RepeatingHeader
+ CertSize
;
278 if (DataSize
== sizeof *SingleHeader
) {
279 Status
= EFI_INVALID_PARAMETER
;
281 if (EFI_ERROR (Status
)) {
285 Data
= AllocatePool (DataSize
);
287 Status
= EFI_OUT_OF_RESOURCES
;
293 SingleHeader
= (SINGLE_HEADER
*)Position
;
294 Status
= gRT
->GetTime (&SingleHeader
->TimeStamp
, NULL
);
295 if (EFI_ERROR (Status
)) {
298 SingleHeader
->TimeStamp
.Pad1
= 0;
299 SingleHeader
->TimeStamp
.Nanosecond
= 0;
300 SingleHeader
->TimeStamp
.TimeZone
= 0;
301 SingleHeader
->TimeStamp
.Daylight
= 0;
302 SingleHeader
->TimeStamp
.Pad2
= 0;
304 SingleHeader
->dwLength
= DataSize
- sizeof SingleHeader
->TimeStamp
;
307 // This looks like a bug in edk2. According to the UEFI specification,
308 // dwLength is "The length of the entire certificate, including the length of
309 // the header, in bytes". That shouldn't stop right after CertType -- it
310 // should include everything below it.
312 SingleHeader
->dwLength
= sizeof *SingleHeader
313 - sizeof SingleHeader
->TimeStamp
;
315 SingleHeader
->wRevision
= 0x0200;
316 SingleHeader
->wCertificateType
= WIN_CERT_TYPE_EFI_GUID
;
317 CopyGuid (&SingleHeader
->CertType
, &gEfiCertPkcs7Guid
);
318 Position
+= sizeof *SingleHeader
;
320 VA_START (Marker
, CertType
);
321 for (Cert
= VA_ARG (Marker
, CONST UINT8
*);
323 Cert
= VA_ARG (Marker
, CONST UINT8
*)) {
325 CONST EFI_GUID
*OwnerGuid
;
327 CertSize
= VA_ARG (Marker
, UINTN
);
328 OwnerGuid
= VA_ARG (Marker
, CONST EFI_GUID
*);
330 RepeatingHeader
= (REPEATING_HEADER
*)Position
;
331 CopyGuid (&RepeatingHeader
->SignatureType
, CertType
);
332 RepeatingHeader
->SignatureListSize
=
333 (UINT32
)(sizeof *RepeatingHeader
+ CertSize
);
334 RepeatingHeader
->SignatureHeaderSize
= 0;
335 RepeatingHeader
->SignatureSize
=
336 (UINT32
)(sizeof RepeatingHeader
->SignatureOwner
+ CertSize
);
337 CopyGuid (&RepeatingHeader
->SignatureOwner
, OwnerGuid
);
338 Position
+= sizeof *RepeatingHeader
;
340 CopyMem (Position
, Cert
, CertSize
);
341 Position
+= CertSize
;
345 ASSERT (Data
+ DataSize
== Position
);
347 Status
= gRT
->SetVariable (VariableName
, VendorGuid
,
348 (EFI_VARIABLE_NON_VOLATILE
|
349 EFI_VARIABLE_BOOTSERVICE_ACCESS
|
350 EFI_VARIABLE_RUNTIME_ACCESS
|
351 EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS
),
358 if (EFI_ERROR (Status
)) {
359 AsciiPrint ("error: %a(\"%s\", %g): %r\n", __FUNCTION__
, VariableName
,
367 Read a UEFI variable into a caller-allocated buffer, enforcing an exact size.
369 @param[in] VariableName The name of the variable to read; passed to
372 @param[in] VendorGuid The vendor (namespace) GUID of the variable to read;
373 passed to gRT->GetVariable().
375 @param[out] Data The caller-allocated buffer that is supposed to
376 receive the variable's contents. On error, the
377 contents of Data are indeterminate.
379 @param[in] DataSize The size in bytes that the caller requires the UEFI
380 variable to have. The caller is responsible for
381 providing room for DataSize bytes in Data.
383 @param[in] AllowMissing If FALSE, the variable is required to exist. If
384 TRUE, the variable is permitted to be missing.
386 @retval EFI_SUCCESS The UEFI variable exists, has the required size
387 (DataSize), and has been read into Data.
389 @retval EFI_SUCCESS The UEFI variable doesn't exist, and
390 AllowMissing is TRUE. DataSize bytes in Data
391 have been zeroed out.
393 @retval EFI_NOT_FOUND The UEFI variable doesn't exist, and
394 AllowMissing is FALSE.
396 @retval EFI_BUFFER_TOO_SMALL The UEFI variable exists, but its size is
397 greater than DataSize.
399 @retval EFI_PROTOCOL_ERROR The UEFI variable exists, but its size is
400 smaller than DataSize.
402 @return Error codes propagated from gRT->GetVariable().
407 IN CHAR16
*VariableName
,
408 IN EFI_GUID
*VendorGuid
,
411 IN BOOLEAN AllowMissing
418 Status
= gRT
->GetVariable (VariableName
, VendorGuid
, NULL
, &Size
, Data
);
419 if (EFI_ERROR (Status
)) {
420 if (Status
== EFI_NOT_FOUND
&& AllowMissing
) {
421 ZeroMem (Data
, DataSize
);
425 AsciiPrint ("error: GetVariable(\"%s\", %g): %r\n", VariableName
,
430 if (Size
!= DataSize
) {
431 AsciiPrint ("error: GetVariable(\"%s\", %g): expected size 0x%Lx, "
432 "got 0x%Lx\n", VariableName
, VendorGuid
, (UINT64
)DataSize
, (UINT64
)Size
);
433 return EFI_PROTOCOL_ERROR
;
441 Populate a SETTINGS structure from the underlying UEFI variables.
443 The following UEFI variables are standard variables:
444 - L"SetupMode" (EFI_SETUP_MODE_NAME)
445 - L"SecureBoot" (EFI_SECURE_BOOT_MODE_NAME)
446 - L"VendorKeys" (EFI_VENDOR_KEYS_VARIABLE_NAME)
448 The following UEFI variables are edk2 extensions:
449 - L"SecureBootEnable" (EFI_SECURE_BOOT_ENABLE_NAME)
450 - L"CustomMode" (EFI_CUSTOM_MODE_NAME)
452 The L"SecureBootEnable" UEFI variable is permitted to be missing, in which
453 case the corresponding field in the SETTINGS object will be zeroed out. The
454 rest of the covered UEFI variables are required to exist; otherwise, the
457 @param[out] Settings The SETTINGS object to fill.
459 @retval EFI_SUCCESS Settings has been populated.
461 @return Error codes propagated from the GetExact() function. The
462 contents of Settings are indeterminate.
467 OUT SETTINGS
*Settings
472 Status
= GetExact (EFI_SETUP_MODE_NAME
, &gEfiGlobalVariableGuid
,
473 &Settings
->SetupMode
, sizeof Settings
->SetupMode
, FALSE
);
474 if (EFI_ERROR (Status
)) {
478 Status
= GetExact (EFI_SECURE_BOOT_MODE_NAME
, &gEfiGlobalVariableGuid
,
479 &Settings
->SecureBoot
, sizeof Settings
->SecureBoot
, FALSE
);
480 if (EFI_ERROR (Status
)) {
484 Status
= GetExact (EFI_SECURE_BOOT_ENABLE_NAME
,
485 &gEfiSecureBootEnableDisableGuid
, &Settings
->SecureBootEnable
,
486 sizeof Settings
->SecureBootEnable
, TRUE
);
487 if (EFI_ERROR (Status
)) {
491 Status
= GetExact (EFI_CUSTOM_MODE_NAME
, &gEfiCustomModeEnableGuid
,
492 &Settings
->CustomMode
, sizeof Settings
->CustomMode
, FALSE
);
493 if (EFI_ERROR (Status
)) {
497 Status
= GetExact (EFI_VENDOR_KEYS_VARIABLE_NAME
, &gEfiGlobalVariableGuid
,
498 &Settings
->VendorKeys
, sizeof Settings
->VendorKeys
, FALSE
);
504 Print the contents of a SETTINGS structure to the UEFI console.
506 @param[in] Settings The SETTINGS object to print the contents of.
511 IN CONST SETTINGS
*Settings
514 AsciiPrint ("info: SetupMode=%d SecureBoot=%d SecureBootEnable=%d "
515 "CustomMode=%d VendorKeys=%d\n", Settings
->SetupMode
, Settings
->SecureBoot
,
516 Settings
->SecureBootEnable
, Settings
->CustomMode
, Settings
->VendorKeys
);
521 Entry point function of this shell application.
537 if (Argc
== 2 && StrCmp (Argv
[1], L
"--no-default") == 0) {
544 // Prepare for failure.
549 // If we're not in Setup Mode, we can't do anything.
551 Status
= GetSettings (&Settings
);
552 if (EFI_ERROR (Status
)) {
555 PrintSettings (&Settings
);
557 if (Settings
.SetupMode
!= 1) {
558 AsciiPrint ("error: already in User Mode\n");
563 // Set PkKek1 and SizeOfPkKek1 to suppress incorrect compiler/analyzer
570 // Fetch the X509 certificate (to be used as Platform Key and first Key
571 // Exchange Key) from SMBIOS.
573 Status
= GetPkKek1 (&PkKek1
, &SizeOfPkKek1
);
574 if (EFI_ERROR (Status
)) {
579 // Enter Custom Mode so we can enroll PK, KEK, db, and dbx without signature
580 // checks on those variable writes.
582 if (Settings
.CustomMode
!= CUSTOM_SECURE_BOOT_MODE
) {
583 Settings
.CustomMode
= CUSTOM_SECURE_BOOT_MODE
;
584 Status
= gRT
->SetVariable (EFI_CUSTOM_MODE_NAME
, &gEfiCustomModeEnableGuid
,
585 (EFI_VARIABLE_NON_VOLATILE
|
586 EFI_VARIABLE_BOOTSERVICE_ACCESS
),
587 sizeof Settings
.CustomMode
, &Settings
.CustomMode
);
588 if (EFI_ERROR (Status
)) {
589 AsciiPrint ("error: SetVariable(\"%s\", %g): %r\n", EFI_CUSTOM_MODE_NAME
,
590 &gEfiCustomModeEnableGuid
, Status
);
599 Status
= EnrollListOfCerts (
600 EFI_IMAGE_SECURITY_DATABASE
,
601 &gEfiImageSecurityDatabaseGuid
,
603 PkKek1
, SizeOfPkKek1
, &gEfiCallerIdGuid
,
606 Status
= EnrollListOfCerts (
607 EFI_IMAGE_SECURITY_DATABASE
,
608 &gEfiImageSecurityDatabaseGuid
,
610 mMicrosoftPca
, mSizeOfMicrosoftPca
, &gMicrosoftVendorGuid
,
611 mMicrosoftUefiCa
, mSizeOfMicrosoftUefiCa
, &gMicrosoftVendorGuid
,
614 if (EFI_ERROR (Status
)) {
621 Status
= EnrollListOfCerts (
622 EFI_IMAGE_SECURITY_DATABASE1
,
623 &gEfiImageSecurityDatabaseGuid
,
625 mSha256OfDevNull
, mSizeOfSha256OfDevNull
, &gEfiCallerIdGuid
,
627 if (EFI_ERROR (Status
)) {
635 Status
= EnrollListOfCerts (
636 EFI_KEY_EXCHANGE_KEY_NAME
,
637 &gEfiGlobalVariableGuid
,
639 PkKek1
, SizeOfPkKek1
, &gEfiCallerIdGuid
,
642 Status
= EnrollListOfCerts (
643 EFI_KEY_EXCHANGE_KEY_NAME
,
644 &gEfiGlobalVariableGuid
,
646 PkKek1
, SizeOfPkKek1
, &gEfiCallerIdGuid
,
647 mMicrosoftKek
, mSizeOfMicrosoftKek
, &gMicrosoftVendorGuid
,
650 if (EFI_ERROR (Status
)) {
655 // Enroll PK, leaving Setup Mode (entering User Mode) at once.
657 Status
= EnrollListOfCerts (
658 EFI_PLATFORM_KEY_NAME
,
659 &gEfiGlobalVariableGuid
,
661 PkKek1
, SizeOfPkKek1
, &gEfiGlobalVariableGuid
,
663 if (EFI_ERROR (Status
)) {
668 // Leave Custom Mode, so that updates to PK, KEK, db, and dbx require valid
671 Settings
.CustomMode
= STANDARD_SECURE_BOOT_MODE
;
672 Status
= gRT
->SetVariable (EFI_CUSTOM_MODE_NAME
, &gEfiCustomModeEnableGuid
,
673 EFI_VARIABLE_NON_VOLATILE
| EFI_VARIABLE_BOOTSERVICE_ACCESS
,
674 sizeof Settings
.CustomMode
, &Settings
.CustomMode
);
675 if (EFI_ERROR (Status
)) {
676 AsciiPrint ("error: SetVariable(\"%s\", %g): %r\n", EFI_CUSTOM_MODE_NAME
,
677 &gEfiCustomModeEnableGuid
, Status
);
682 // Final sanity check:
685 // (read-only, standardized by UEFI)
689 // PK enrolled no PK enrolled yet,
690 // (this is called "User Mode") PK enrollment possible
693 // [SecureBootEnable]
694 // (read-write, edk2-specific, boot service only)
698 // [SecureBoot]=0 [SecureBoot]=1
699 // (read-only, standardized by UEFI) (read-only, standardized by UEFI)
700 // images are not verified images are verified, platform is
701 // operating in Secure Boot mode
705 // (read-write, edk2-specific, boot service only)
709 // PK, KEK, db, dbx PK, KEK, db, dbx
710 // updates are verified updates are not verified
712 Status
= GetSettings (&Settings
);
713 if (EFI_ERROR (Status
)) {
716 PrintSettings (&Settings
);
718 if (Settings
.SetupMode
!= 0 || Settings
.SecureBoot
!= 1 ||
719 Settings
.SecureBootEnable
!= 1 || Settings
.CustomMode
!= 0 ||
720 Settings
.VendorKeys
!= 0) {
721 AsciiPrint ("error: unexpected\n");
725 AsciiPrint ("info: success\n");