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"
28 Fetch the X509 certificate (to be used as Platform Key and first Key Exchange
31 @param[out] PkKek1 The X509 certificate in DER encoding from the
32 hypervisor, to be enrolled as PK and first KEK
33 entry. On success, the caller is responsible for
34 releasing PkKek1 with FreePool().
36 @param[out] SizeOfPkKek1 The size of PkKek1 in bytes.
38 @retval EFI_SUCCESS PkKek1 and SizeOfPkKek1 have been set
41 @retval EFI_NOT_FOUND An OEM String matching
42 OVMF_PK_KEK1_APP_PREFIX_GUID has not been
45 @retval EFI_PROTOCOL_ERROR In the OEM String matching
46 OVMF_PK_KEK1_APP_PREFIX_GUID, the certificate
47 is empty, or it has invalid base64 encoding.
49 @retval EFI_OUT_OF_RESOURCES Memory allocation failed.
51 @return Error codes from gBS->LocateProtocol().
57 OUT UINTN
*SizeOfPkKek1
60 CONST CHAR8
*Base64Cert
;
61 CHAR8 OvmfPkKek1AppPrefix
[GUID_STRING_LENGTH
+ 1 + 1];
63 EFI_SMBIOS_PROTOCOL
*Smbios
;
64 EFI_SMBIOS_HANDLE Handle
;
66 EFI_SMBIOS_TABLE_HEADER
*Header
;
67 SMBIOS_TABLE_TYPE11
*OemStringsTable
;
69 UINTN DecodedCertSize
;
75 // Format the application prefix, for OEM String matching.
79 sizeof OvmfPkKek1AppPrefix
,
81 &gOvmfPkKek1AppPrefixGuid
85 // Scan all "OEM Strings" tables.
87 Status
= gBS
->LocateProtocol (
88 &gEfiSmbiosProtocolGuid
,
92 if (EFI_ERROR (Status
)) {
93 AsciiPrint ("error: failed to locate EFI_SMBIOS_PROTOCOL: %r\n", Status
);
97 Handle
= SMBIOS_HANDLE_PI_RESERVED
;
98 Type
= SMBIOS_TYPE_OEM_STRINGS
;
99 for (Status
= Smbios
->GetNext (Smbios
, &Handle
, &Type
, &Header
, NULL
);
101 Status
= Smbios
->GetNext (Smbios
, &Handle
, &Type
, &Header
, NULL
))
103 CONST CHAR8
*OemString
;
106 if (Header
->Length
< sizeof *OemStringsTable
) {
108 // Malformed table header, skip to next.
113 OemStringsTable
= (SMBIOS_TABLE_TYPE11
*)Header
;
116 // Scan all strings in the unformatted area of the current "OEM Strings"
119 OemString
= (CONST CHAR8
*)(OemStringsTable
+ 1);
120 for (Idx
= 0; Idx
< OemStringsTable
->StringCount
; ++Idx
) {
121 CHAR8 CandidatePrefix
[sizeof OvmfPkKek1AppPrefix
];
124 // NUL-terminate the candidate prefix for case-insensitive comparison.
128 sizeof CandidatePrefix
,
130 GUID_STRING_LENGTH
+ 1
132 if (AsciiStriCmp (OvmfPkKek1AppPrefix
, CandidatePrefix
) == 0) {
134 // The current string matches the prefix.
136 Base64Cert
= OemString
+ GUID_STRING_LENGTH
+ 1;
140 OemString
+= AsciiStrSize (OemString
);
143 if (Idx
< OemStringsTable
->StringCount
) {
145 // The current table has a matching string.
151 if (EFI_ERROR (Status
)) {
153 // No table with a matching string has been found.
156 "error: OEM String with app prefix %g not found: %r\n",
157 &gOvmfPkKek1AppPrefixGuid
,
160 return EFI_NOT_FOUND
;
163 ASSERT (Base64Cert
!= NULL
);
164 Base64CertLen
= AsciiStrLen (Base64Cert
);
167 // Verify the base64 encoding, and determine the decoded size.
170 Status
= Base64Decode (Base64Cert
, Base64CertLen
, NULL
, &DecodedCertSize
);
172 case EFI_BUFFER_TOO_SMALL
:
173 ASSERT (DecodedCertSize
> 0);
177 "error: empty certificate after app prefix %g\n",
178 &gOvmfPkKek1AppPrefixGuid
180 return EFI_PROTOCOL_ERROR
;
183 "error: invalid base64 string after app prefix %g\n",
184 &gOvmfPkKek1AppPrefixGuid
186 return EFI_PROTOCOL_ERROR
;
190 // Allocate the output buffer.
192 DecodedCert
= AllocatePool (DecodedCertSize
);
193 if (DecodedCert
== NULL
) {
194 AsciiPrint ("error: failed to allocate memory\n");
195 return EFI_OUT_OF_RESOURCES
;
199 // Decoding will succeed at this point.
201 Status
= Base64Decode (
207 ASSERT_EFI_ERROR (Status
);
209 *PkKek1
= DecodedCert
;
210 *SizeOfPkKek1
= DecodedCertSize
;
215 Enroll a set of certificates in a global variable, overwriting it.
217 The variable will be rewritten with NV+BS+RT+AT attributes.
219 @param[in] VariableName The name of the variable to overwrite.
221 @param[in] VendorGuid The namespace (ie. vendor GUID) of the variable to
224 @param[in] CertType The GUID determining the type of all the
225 certificates in the set that is passed in. For
226 example, gEfiCertX509Guid stands for DER-encoded
227 X.509 certificates, while gEfiCertSha256Guid stands
228 for SHA256 image hashes.
230 @param[in] ... A list of
232 IN CONST UINT8 *Cert,
234 IN CONST EFI_GUID *OwnerGuid
236 triplets. If the first component of a triplet is
237 NULL, then the other two components are not
238 accessed, and processing is terminated. The list of
239 certificates is enrolled in the variable specified,
240 overwriting it. The OwnerGuid component identifies
241 the agent installing the certificate.
243 @retval EFI_INVALID_PARAMETER The triplet list is empty (ie. the first Cert
244 value is NULL), or one of the CertSize values
245 is 0, or one of the CertSize values would
246 overflow the accumulated UINT32 data size.
248 @retval EFI_OUT_OF_RESOURCES Out of memory while formatting variable
251 @retval EFI_SUCCESS Enrollment successful; the variable has been
252 overwritten (or created).
254 @return Error codes from gRT->GetTime() and
261 IN CHAR16
*VariableName
,
262 IN EFI_GUID
*VendorGuid
,
263 IN EFI_GUID
*CertType
,
268 SINGLE_HEADER
*SingleHeader
;
269 REPEATING_HEADER
*RepeatingHeader
;
276 Status
= EFI_SUCCESS
;
279 // compute total size first, for UINT32 range check, and allocation
281 DataSize
= sizeof *SingleHeader
;
282 VA_START (Marker
, CertType
);
283 for (Cert
= VA_ARG (Marker
, CONST UINT8
*);
285 Cert
= VA_ARG (Marker
, CONST UINT8
*))
289 CertSize
= VA_ARG (Marker
, UINTN
);
290 (VOID
)VA_ARG (Marker
, CONST EFI_GUID
*);
292 if ((CertSize
== 0) ||
293 (CertSize
> MAX_UINT32
- sizeof *RepeatingHeader
) ||
294 (DataSize
> MAX_UINT32
- sizeof *RepeatingHeader
- CertSize
))
296 Status
= EFI_INVALID_PARAMETER
;
300 DataSize
+= sizeof *RepeatingHeader
+ CertSize
;
305 if (DataSize
== sizeof *SingleHeader
) {
306 Status
= EFI_INVALID_PARAMETER
;
309 if (EFI_ERROR (Status
)) {
313 Data
= AllocatePool (DataSize
);
315 Status
= EFI_OUT_OF_RESOURCES
;
321 SingleHeader
= (SINGLE_HEADER
*)Position
;
322 Status
= gRT
->GetTime (&SingleHeader
->TimeStamp
, NULL
);
323 if (EFI_ERROR (Status
)) {
327 SingleHeader
->TimeStamp
.Pad1
= 0;
328 SingleHeader
->TimeStamp
.Nanosecond
= 0;
329 SingleHeader
->TimeStamp
.TimeZone
= 0;
330 SingleHeader
->TimeStamp
.Daylight
= 0;
331 SingleHeader
->TimeStamp
.Pad2
= 0;
333 SingleHeader
->dwLength
= DataSize
- sizeof SingleHeader
->TimeStamp
;
336 // This looks like a bug in edk2. According to the UEFI specification,
337 // dwLength is "The length of the entire certificate, including the length of
338 // the header, in bytes". That shouldn't stop right after CertType -- it
339 // should include everything below it.
341 SingleHeader
->dwLength
= sizeof *SingleHeader
342 - sizeof SingleHeader
->TimeStamp
;
344 SingleHeader
->wRevision
= 0x0200;
345 SingleHeader
->wCertificateType
= WIN_CERT_TYPE_EFI_GUID
;
346 CopyGuid (&SingleHeader
->CertType
, &gEfiCertPkcs7Guid
);
347 Position
+= sizeof *SingleHeader
;
349 VA_START (Marker
, CertType
);
350 for (Cert
= VA_ARG (Marker
, CONST UINT8
*);
352 Cert
= VA_ARG (Marker
, CONST UINT8
*))
355 CONST EFI_GUID
*OwnerGuid
;
357 CertSize
= VA_ARG (Marker
, UINTN
);
358 OwnerGuid
= VA_ARG (Marker
, CONST EFI_GUID
*);
360 RepeatingHeader
= (REPEATING_HEADER
*)Position
;
361 CopyGuid (&RepeatingHeader
->SignatureType
, CertType
);
362 RepeatingHeader
->SignatureListSize
=
363 (UINT32
)(sizeof *RepeatingHeader
+ CertSize
);
364 RepeatingHeader
->SignatureHeaderSize
= 0;
365 RepeatingHeader
->SignatureSize
=
366 (UINT32
)(sizeof RepeatingHeader
->SignatureOwner
+ CertSize
);
367 CopyGuid (&RepeatingHeader
->SignatureOwner
, OwnerGuid
);
368 Position
+= sizeof *RepeatingHeader
;
370 CopyMem (Position
, Cert
, CertSize
);
371 Position
+= CertSize
;
376 ASSERT (Data
+ DataSize
== Position
);
378 Status
= gRT
->SetVariable (
381 (EFI_VARIABLE_NON_VOLATILE
|
382 EFI_VARIABLE_BOOTSERVICE_ACCESS
|
383 EFI_VARIABLE_RUNTIME_ACCESS
|
384 EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS
),
393 if (EFI_ERROR (Status
)) {
395 "error: %a(\"%s\", %g): %r\n",
407 Read a UEFI variable into a caller-allocated buffer, enforcing an exact size.
409 @param[in] VariableName The name of the variable to read; passed to
412 @param[in] VendorGuid The vendor (namespace) GUID of the variable to read;
413 passed to gRT->GetVariable().
415 @param[out] Data The caller-allocated buffer that is supposed to
416 receive the variable's contents. On error, the
417 contents of Data are indeterminate.
419 @param[in] DataSize The size in bytes that the caller requires the UEFI
420 variable to have. The caller is responsible for
421 providing room for DataSize bytes in Data.
423 @param[in] AllowMissing If FALSE, the variable is required to exist. If
424 TRUE, the variable is permitted to be missing.
426 @retval EFI_SUCCESS The UEFI variable exists, has the required size
427 (DataSize), and has been read into Data.
429 @retval EFI_SUCCESS The UEFI variable doesn't exist, and
430 AllowMissing is TRUE. DataSize bytes in Data
431 have been zeroed out.
433 @retval EFI_NOT_FOUND The UEFI variable doesn't exist, and
434 AllowMissing is FALSE.
436 @retval EFI_BUFFER_TOO_SMALL The UEFI variable exists, but its size is
437 greater than DataSize.
439 @retval EFI_PROTOCOL_ERROR The UEFI variable exists, but its size is
440 smaller than DataSize.
442 @return Error codes propagated from gRT->GetVariable().
447 IN CHAR16
*VariableName
,
448 IN EFI_GUID
*VendorGuid
,
451 IN BOOLEAN AllowMissing
458 Status
= gRT
->GetVariable (VariableName
, VendorGuid
, NULL
, &Size
, Data
);
459 if (EFI_ERROR (Status
)) {
460 if ((Status
== EFI_NOT_FOUND
) && AllowMissing
) {
461 ZeroMem (Data
, DataSize
);
466 "error: GetVariable(\"%s\", %g): %r\n",
474 if (Size
!= DataSize
) {
476 "error: GetVariable(\"%s\", %g): expected size 0x%Lx, "
483 return EFI_PROTOCOL_ERROR
;
490 Populate a SETTINGS structure from the underlying UEFI variables.
492 The following UEFI variables are standard variables:
493 - L"SetupMode" (EFI_SETUP_MODE_NAME)
494 - L"SecureBoot" (EFI_SECURE_BOOT_MODE_NAME)
495 - L"VendorKeys" (EFI_VENDOR_KEYS_VARIABLE_NAME)
497 The following UEFI variables are edk2 extensions:
498 - L"SecureBootEnable" (EFI_SECURE_BOOT_ENABLE_NAME)
499 - L"CustomMode" (EFI_CUSTOM_MODE_NAME)
501 The L"SecureBootEnable" UEFI variable is permitted to be missing, in which
502 case the corresponding field in the SETTINGS object will be zeroed out. The
503 rest of the covered UEFI variables are required to exist; otherwise, the
506 @param[out] Settings The SETTINGS object to fill.
508 @retval EFI_SUCCESS Settings has been populated.
510 @return Error codes propagated from the GetExact() function. The
511 contents of Settings are indeterminate.
516 OUT SETTINGS
*Settings
523 &gEfiGlobalVariableGuid
,
524 &Settings
->SetupMode
,
525 sizeof Settings
->SetupMode
,
528 if (EFI_ERROR (Status
)) {
533 EFI_SECURE_BOOT_MODE_NAME
,
534 &gEfiGlobalVariableGuid
,
535 &Settings
->SecureBoot
,
536 sizeof Settings
->SecureBoot
,
539 if (EFI_ERROR (Status
)) {
544 EFI_SECURE_BOOT_ENABLE_NAME
,
545 &gEfiSecureBootEnableDisableGuid
,
546 &Settings
->SecureBootEnable
,
547 sizeof Settings
->SecureBootEnable
,
550 if (EFI_ERROR (Status
)) {
555 EFI_CUSTOM_MODE_NAME
,
556 &gEfiCustomModeEnableGuid
,
557 &Settings
->CustomMode
,
558 sizeof Settings
->CustomMode
,
561 if (EFI_ERROR (Status
)) {
566 EFI_VENDOR_KEYS_VARIABLE_NAME
,
567 &gEfiGlobalVariableGuid
,
568 &Settings
->VendorKeys
,
569 sizeof Settings
->VendorKeys
,
576 Print the contents of a SETTINGS structure to the UEFI console.
578 @param[in] Settings The SETTINGS object to print the contents of.
583 IN CONST SETTINGS
*Settings
587 "info: SetupMode=%d SecureBoot=%d SecureBootEnable=%d "
588 "CustomMode=%d VendorKeys=%d\n",
590 Settings
->SecureBoot
,
591 Settings
->SecureBootEnable
,
592 Settings
->CustomMode
,
598 Entry point function of this shell application.
614 if ((Argc
== 2) && (StrCmp (Argv
[1], L
"--no-default") == 0)) {
621 // Prepare for failure.
626 // If we're not in Setup Mode, we can't do anything.
628 Status
= GetSettings (&Settings
);
629 if (EFI_ERROR (Status
)) {
633 PrintSettings (&Settings
);
635 if (Settings
.SetupMode
!= 1) {
636 AsciiPrint ("error: already in User Mode\n");
641 // Set PkKek1 and SizeOfPkKek1 to suppress incorrect compiler/analyzer
648 // Fetch the X509 certificate (to be used as Platform Key and first Key
649 // Exchange Key) from SMBIOS.
651 Status
= GetPkKek1 (&PkKek1
, &SizeOfPkKek1
);
652 if (EFI_ERROR (Status
)) {
657 // Enter Custom Mode so we can enroll PK, KEK, db, and dbx without signature
658 // checks on those variable writes.
660 if (Settings
.CustomMode
!= CUSTOM_SECURE_BOOT_MODE
) {
661 Settings
.CustomMode
= CUSTOM_SECURE_BOOT_MODE
;
662 Status
= gRT
->SetVariable (
663 EFI_CUSTOM_MODE_NAME
,
664 &gEfiCustomModeEnableGuid
,
665 (EFI_VARIABLE_NON_VOLATILE
|
666 EFI_VARIABLE_BOOTSERVICE_ACCESS
),
667 sizeof Settings
.CustomMode
,
670 if (EFI_ERROR (Status
)) {
672 "error: SetVariable(\"%s\", %g): %r\n",
673 EFI_CUSTOM_MODE_NAME
,
674 &gEfiCustomModeEnableGuid
,
685 Status
= EnrollListOfCerts (
686 EFI_IMAGE_SECURITY_DATABASE
,
687 &gEfiImageSecurityDatabaseGuid
,
695 Status
= EnrollListOfCerts (
696 EFI_IMAGE_SECURITY_DATABASE
,
697 &gEfiImageSecurityDatabaseGuid
,
701 &gMicrosoftVendorGuid
,
703 mSizeOfMicrosoftUefiCa
,
704 &gMicrosoftVendorGuid
,
709 if (EFI_ERROR (Status
)) {
716 Status
= EnrollListOfCerts (
717 EFI_IMAGE_SECURITY_DATABASE1
,
718 &gEfiImageSecurityDatabaseGuid
,
721 mSizeOfSha256OfDevNull
,
725 if (EFI_ERROR (Status
)) {
733 Status
= EnrollListOfCerts (
734 EFI_KEY_EXCHANGE_KEY_NAME
,
735 &gEfiGlobalVariableGuid
,
743 Status
= EnrollListOfCerts (
744 EFI_KEY_EXCHANGE_KEY_NAME
,
745 &gEfiGlobalVariableGuid
,
752 &gMicrosoftVendorGuid
,
757 if (EFI_ERROR (Status
)) {
762 // Enroll PK, leaving Setup Mode (entering User Mode) at once.
764 Status
= EnrollListOfCerts (
765 EFI_PLATFORM_KEY_NAME
,
766 &gEfiGlobalVariableGuid
,
770 &gEfiGlobalVariableGuid
,
773 if (EFI_ERROR (Status
)) {
778 // Leave Custom Mode, so that updates to PK, KEK, db, and dbx require valid
781 Settings
.CustomMode
= STANDARD_SECURE_BOOT_MODE
;
782 Status
= gRT
->SetVariable (
783 EFI_CUSTOM_MODE_NAME
,
784 &gEfiCustomModeEnableGuid
,
785 EFI_VARIABLE_NON_VOLATILE
| EFI_VARIABLE_BOOTSERVICE_ACCESS
,
786 sizeof Settings
.CustomMode
,
789 if (EFI_ERROR (Status
)) {
791 "error: SetVariable(\"%s\", %g): %r\n",
792 EFI_CUSTOM_MODE_NAME
,
793 &gEfiCustomModeEnableGuid
,
800 // Final sanity check:
803 // (read-only, standardized by UEFI)
807 // PK enrolled no PK enrolled yet,
808 // (this is called "User Mode") PK enrollment possible
811 // [SecureBootEnable]
812 // (read-write, edk2-specific, boot service only)
816 // [SecureBoot]=0 [SecureBoot]=1
817 // (read-only, standardized by UEFI) (read-only, standardized by UEFI)
818 // images are not verified images are verified, platform is
819 // operating in Secure Boot mode
823 // (read-write, edk2-specific, boot service only)
827 // PK, KEK, db, dbx PK, KEK, db, dbx
828 // updates are verified updates are not verified
830 Status
= GetSettings (&Settings
);
831 if (EFI_ERROR (Status
)) {
835 PrintSettings (&Settings
);
837 if ((Settings
.SetupMode
!= 0) || (Settings
.SecureBoot
!= 1) ||
838 (Settings
.SecureBootEnable
!= 1) || (Settings
.CustomMode
!= 0) ||
839 (Settings
.VendorKeys
!= 0))
841 AsciiPrint ("error: unexpected\n");
845 AsciiPrint ("info: success\n");