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 if (Argc
== 2 && StrCmp (Argv
[1], L
"--no-default") == 0) {
550 // Prepare for failure.
555 // If we're not in Setup Mode, we can't do anything.
557 Status
= GetSettings (&Settings
);
558 if (EFI_ERROR (Status
)) {
561 PrintSettings (&Settings
);
563 if (Settings
.SetupMode
!= 1) {
564 AsciiPrint ("error: already in User Mode\n");
569 // Set PkKek1 and SizeOfPkKek1 to suppress incorrect compiler/analyzer
576 // Fetch the X509 certificate (to be used as Platform Key and first Key
577 // Exchange Key) from SMBIOS.
579 Status
= GetPkKek1 (&PkKek1
, &SizeOfPkKek1
);
580 if (EFI_ERROR (Status
)) {
585 // Enter Custom Mode so we can enroll PK, KEK, db, and dbx without signature
586 // checks on those variable writes.
588 if (Settings
.CustomMode
!= CUSTOM_SECURE_BOOT_MODE
) {
589 Settings
.CustomMode
= CUSTOM_SECURE_BOOT_MODE
;
590 Status
= gRT
->SetVariable (EFI_CUSTOM_MODE_NAME
, &gEfiCustomModeEnableGuid
,
591 (EFI_VARIABLE_NON_VOLATILE
|
592 EFI_VARIABLE_BOOTSERVICE_ACCESS
),
593 sizeof Settings
.CustomMode
, &Settings
.CustomMode
);
594 if (EFI_ERROR (Status
)) {
595 AsciiPrint ("error: SetVariable(\"%s\", %g): %r\n", EFI_CUSTOM_MODE_NAME
,
596 &gEfiCustomModeEnableGuid
, Status
);
605 Status
= EnrollListOfCerts (
606 EFI_IMAGE_SECURITY_DATABASE
,
607 &gEfiImageSecurityDatabaseGuid
,
609 PkKek1
, SizeOfPkKek1
, &gEfiCallerIdGuid
,
612 Status
= EnrollListOfCerts (
613 EFI_IMAGE_SECURITY_DATABASE
,
614 &gEfiImageSecurityDatabaseGuid
,
616 mMicrosoftPca
, mSizeOfMicrosoftPca
, &gMicrosoftVendorGuid
,
617 mMicrosoftUefiCa
, mSizeOfMicrosoftUefiCa
, &gMicrosoftVendorGuid
,
620 if (EFI_ERROR (Status
)) {
627 Status
= EnrollListOfCerts (
628 EFI_IMAGE_SECURITY_DATABASE1
,
629 &gEfiImageSecurityDatabaseGuid
,
631 mSha256OfDevNull
, mSizeOfSha256OfDevNull
, &gEfiCallerIdGuid
,
633 if (EFI_ERROR (Status
)) {
641 Status
= EnrollListOfCerts (
642 EFI_KEY_EXCHANGE_KEY_NAME
,
643 &gEfiGlobalVariableGuid
,
645 PkKek1
, SizeOfPkKek1
, &gEfiCallerIdGuid
,
648 Status
= EnrollListOfCerts (
649 EFI_KEY_EXCHANGE_KEY_NAME
,
650 &gEfiGlobalVariableGuid
,
652 PkKek1
, SizeOfPkKek1
, &gEfiCallerIdGuid
,
653 mMicrosoftKek
, mSizeOfMicrosoftKek
, &gMicrosoftVendorGuid
,
656 if (EFI_ERROR (Status
)) {
661 // Enroll PK, leaving Setup Mode (entering User Mode) at once.
663 Status
= EnrollListOfCerts (
664 EFI_PLATFORM_KEY_NAME
,
665 &gEfiGlobalVariableGuid
,
667 PkKek1
, SizeOfPkKek1
, &gEfiGlobalVariableGuid
,
669 if (EFI_ERROR (Status
)) {
674 // Leave Custom Mode, so that updates to PK, KEK, db, and dbx require valid
677 Settings
.CustomMode
= STANDARD_SECURE_BOOT_MODE
;
678 Status
= gRT
->SetVariable (EFI_CUSTOM_MODE_NAME
, &gEfiCustomModeEnableGuid
,
679 EFI_VARIABLE_NON_VOLATILE
| EFI_VARIABLE_BOOTSERVICE_ACCESS
,
680 sizeof Settings
.CustomMode
, &Settings
.CustomMode
);
681 if (EFI_ERROR (Status
)) {
682 AsciiPrint ("error: SetVariable(\"%s\", %g): %r\n", EFI_CUSTOM_MODE_NAME
,
683 &gEfiCustomModeEnableGuid
, Status
);
688 // Final sanity check:
691 // (read-only, standardized by UEFI)
695 // PK enrolled no PK enrolled yet,
696 // (this is called "User Mode") PK enrollment possible
699 // [SecureBootEnable]
700 // (read-write, edk2-specific, boot service only)
704 // [SecureBoot]=0 [SecureBoot]=1
705 // (read-only, standardized by UEFI) (read-only, standardized by UEFI)
706 // images are not verified images are verified, platform is
707 // operating in Secure Boot mode
711 // (read-write, edk2-specific, boot service only)
715 // PK, KEK, db, dbx PK, KEK, db, dbx
716 // updates are verified updates are not verified
718 Status
= GetSettings (&Settings
);
719 if (EFI_ERROR (Status
)) {
722 PrintSettings (&Settings
);
724 if (Settings
.SetupMode
!= 0 || Settings
.SecureBoot
!= 1 ||
725 Settings
.SecureBootEnable
!= 1 || Settings
.CustomMode
!= 0 ||
726 Settings
.VendorKeys
!= 0) {
727 AsciiPrint ("error: unexpected\n");
731 AsciiPrint ("info: success\n");