Skip to content

Commit 8bc2a73

Browse files
committed
Add workaround for buggy AMI firmwares where appending to KEK fails
* Multiple user reports, as well as our own testing, seems to indicate that some old-ish AMI UEFI firmwares on the likes of Lenovo, ASUS, Dell, MSI and HP, will produce an error (EFI_INVALID_PARAMETER) when trying to append to the KEK Secure Boot variables, if a KEK variable has already been set. * This means that, on the affected machines, when we try to add the CA 2011 and CA 2023 MS KEKs, as two separate SetVariable() operations, the second operation fails (and this is regardless of what KEK we add first, or whether we generate the ESL internally from the cert, or apply an externally generated ESL, which hints that this is *not* an issue that is internal to Mosby, even more so as we also see a similar issue with efitools' KeyTool). * To work around this, we now merge the KEKs into a single, multi-entry ESL, before applying it in a single SetVariable() operation (which is facilitated by the fact that you can simply concatenate ESLs together to obtain an ESL array that UEFI happily processes as multiple entries). * Closes #14.
1 parent 18ff4b3 commit 8bc2a73

File tree

1 file changed

+66
-0
lines changed

1 file changed

+66
-0
lines changed

src/mosby.c

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -224,6 +224,11 @@ EFI_STATUS EFIAPI efi_main(
224224
BOOLEAN Append = FALSE, Reboot = FALSE, LogToFile = TRUE;
225225
EFI_STATUS Status;
226226
EFI_TIME Time;
227+
#if defined(_M_X64) || defined(__x86_64__)
228+
EFI_SIGNATURE_LIST* Esl[8] = { 0 };
229+
UINTN EslIndex, EslOffset;
230+
UINT8 *MergedEsl = NULL;
231+
#endif
227232
UINT8 Set = MOSBY_SET1;
228233
UINTN i, Size;
229234
UINT16* SystemSSPV = NULL;
@@ -550,6 +555,64 @@ EFI_STATUS EFIAPI efi_main(
550555
List.Size++;
551556
}
552557

558+
#if defined(_M_X64) || defined(__x86_64__)
559+
/*
560+
* There appears to be a whole sway of AMI UEFI firmwares with a rather unfortunate bug,
561+
* that prevents appending to an existing KEK store. Which means that, on the affected
562+
* systems, if we try to write more than one KEK, using multiple SetVariable() calls,
563+
* only the first call succeeds and all subsequent ones fail with EFI_INVALID_PARAMETER.
564+
* To work around this and since any unsigned KEK we processed above should already have
565+
* been converted to an ESL (embedded in an EFI_VARIABLE_AUTHENTICATION_2 struct), we
566+
* concatenate all these ESLs into a single array, which can then be written through a
567+
* single SetVariable() operation.
568+
* For more on this, see https://github.com/pbatard/Mosby/issues/14.
569+
*/
570+
EslIndex = 0;
571+
for (i = 0; i < List.Size; i++) {
572+
/* Only process valid KEK entries for which we have a variable */
573+
if (List.Entry[i].Type != KEK || List.Entry[i].Variable.Data == NULL)
574+
continue;
575+
/* Only process variables for which we have an *unsigned* ESL */
576+
if (List.Entry[i].Flags & ALLOW_UPDATE)
577+
continue;
578+
if (EslIndex >= ARRAY_SIZE(Esl))
579+
Abort(EFI_INVALID_PARAMETER, L"More than %d KEKs to merge - Aborting\n", ARRAY_SIZE(Esl));
580+
if (List.Entry[i].Description != NULL)
581+
RecallPrint(L"Adding '%a' to Merged KEK List\n", List.Entry[i].Description);
582+
else
583+
RecallPrint(L"Adding '%s' to Merged KEK List\n", List.Entry[i].Path);
584+
/* Get the ESL data from EFI_VARIABLE_AUTHENTICATION_2 (at .AuthInfo.CertData) */
585+
Esl[EslIndex++] = (EFI_SIGNATURE_LIST*)&((UINT8*)List.Entry[i].Variable.Data)[
586+
OFFSET_OF(EFI_VARIABLE_AUTHENTICATION_2, AuthInfo) +
587+
OFFSET_OF(WIN_CERTIFICATE_UEFI_GUID, CertData)];
588+
/* Remove the individual KEK from our installation list, since it will be merged */
589+
List.Entry[i].Flags |= NO_INSTALL;
590+
}
591+
/* Now concatenate all the ESLs from above into an array and create the variable */
592+
if (EslIndex > 0 && List.Size < ARRAY_SIZE(List.Entry)) {
593+
List.Entry[List.Size].Buffer.Size = 0;
594+
for (i = 0; i < EslIndex; i++)
595+
List.Entry[List.Size].Buffer.Size += Esl[i]->SignatureListSize;
596+
MergedEsl = AllocateZeroPool(List.Entry[List.Size].Buffer.Size);
597+
if (MergedEsl == NULL)
598+
Abort(EFI_OUT_OF_RESOURCES, L"Could not allocate data to merge KEKs\n");
599+
for (EslOffset = 0, i = 0; i < EslIndex; i++) {
600+
CopyMem(&MergedEsl[EslOffset], Esl[i], Esl[i]->SignatureListSize);
601+
EslOffset += Esl[i]->SignatureListSize;
602+
FreePool(Esl[i]);
603+
}
604+
List.Entry[List.Size].Buffer.Data = MergedEsl;
605+
List.Entry[List.Size].Type = KEK;
606+
List.Entry[List.Size].Attrs = UEFI_VAR_NV_BS_RT_TIMEAUTH;
607+
List.Entry[List.Size].Description = "Merged KEK List";
608+
List.Entry[List.Size].Path = L"Merged KEK List";
609+
Status = PopulateAuthVar(&List.Entry[List.Size]);
610+
if (EFI_ERROR(Status))
611+
ReportErrorAndExit(L"Failed to create merged KEK variable - Aborting\n");
612+
List.Size++;
613+
}
614+
#endif
615+
553616
/* EDK2 provides a DeleteSecureBootVariables(), so we might as well call it. */
554617
DeleteSecureBootVariables();
555618

@@ -586,6 +649,9 @@ EFI_STATUS EFIAPI efi_main(
586649
for (i = 0; i < List.Size; i++)
587650
FreePool(List.Entry[i].Variable.Data);
588651
FreePool(Argv);
652+
#if defined(_M_X64) || defined(__x86_64__)
653+
FreePool(MergedEsl);
654+
#endif
589655
RecallPrintFree();
590656
CloseLogger();
591657
if (Reboot) {

0 commit comments

Comments
 (0)