Skip to content

Commit 060f986

Browse files
committed
Always use signed authvars to update variables
* Some poorly designed UEFI firmwares (HP ProDesk 600 G1 yet again) do *not* accept anything that is not signed when writing the Secure Boot variables in Setup Mode. * This means that, with the existing code, only the DBX and PK variables can be written, whereas DB and KEK (which we build as unsigned ESLs from the certs) will fail with EFI_SECURITY_VIOLATION error. * To work around this, we now always sign unsigned variables with the PK credentials we autogenerate. * Addresses part of #17.
1 parent 0457d57 commit 060f986

File tree

4 files changed

+69
-58
lines changed

4 files changed

+69
-58
lines changed

src/mosby.c

Lines changed: 37 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -42,13 +42,8 @@ STATIC EFI_GUID gEfiShimLockGuid =
4242
EFI_GUID gEfiMicrosoftGuid =
4343
{ 0x77FA9ABD, 0x0359, 0x4D32, { 0xBD, 0x60, 0x28, 0xF4, 0xE7, 0x8F, 0x78, 0x4B } };
4444

45-
/* Attributes for the "key" types we support */
46-
STATIC struct {
47-
CHAR8 *DisplayName;
48-
CHAR16 *OptionName;
49-
CHAR16 *VariableName;
50-
EFI_GUID *VariableGuid;
51-
} KeyInfo[MAX_TYPES] = {
45+
/* Populate the key type attributes */
46+
MOSBY_KEY_INFO KeyInfo[MAX_TYPES] = {
5247
[PK] = {
5348
.DisplayName = "PK: ",
5449
.OptionName = L"-pk",
@@ -229,7 +224,7 @@ EFI_STATUS EFIAPI efi_main(
229224
UINT16* SystemSSPV = NULL;
230225
UINT32 SystemSBatVer = 0, InstallSBatVer = 0;
231226
INTN Argc, Type, Sel, LastEntry;
232-
MOSBY_CRED Cred = { 0 };
227+
MOSBY_CRED PkCred = { 0 }, DbCred = { 0 };
233228
CHAR8 DbSubject[80], PkSubject[80], *SBat = NULL, *SBatLine = NULL;
234229
CHAR16 **Argv = NULL, **ArgvCopy, MosbyKeyPath[MAX_PATH];
235230
MOSBY_LIST List;
@@ -414,6 +409,17 @@ EFI_STATUS EFIAPI efi_main(
414409
}
415410

416411
process_binaries:
412+
413+
/* Generate the credentials, which we use both to sign the authvars as well as PK */
414+
Status = gRT->GetTime(&Time, NULL);
415+
if (EFI_ERROR(Status))
416+
ReportErrorAndExit(L"Failed to get time - Aborting\n");
417+
AsciiSPrint(PkSubject, sizeof(PkSubject), "Mosby Generated PK [%04d.%02d.%02d]",
418+
Time.Year, Time.Month, Time.Day);
419+
Status = GenerateCredentials(PkSubject, &PkCred);
420+
if (EFI_ERROR(Status))
421+
ReportErrorAndExit(L"Failed to generate signing credentials - Aborting\n");
422+
417423
/* Process binaries that have been provided to the application */
418424
for (i = 0; i < List.Size; i++) {
419425
if (List.Entry[i].Variable.Data != NULL)
@@ -435,7 +441,7 @@ EFI_STATUS EFIAPI efi_main(
435441
List.Entry[i].Attrs = UEFI_VAR_NV_BS;
436442
break;
437443
default:
438-
Status = PopulateAuthVar(&List.Entry[i]);
444+
Status = PopulateAuthVar(&List.Entry[i], &PkCred);
439445
if (EFI_ERROR(Status))
440446
ReportErrorAndExit(L"Failed to create variable - Aborting\n");
441447
break;
@@ -492,58 +498,44 @@ EFI_STATUS EFIAPI efi_main(
492498
i = List.Size;
493499
RecallPrint(L"Generating Secure Boot signing credentials...\n");
494500
List.Entry[i].Type = DB;
495-
Status = gRT->GetTime(&Time, NULL);
496-
if (EFI_ERROR(Status))
497-
ReportErrorAndExit(L"Failed to get time - Aborting\n");
498501
AsciiSPrint(DbSubject, sizeof(DbSubject), "%a [%04d.%02d.%02d]",
499502
MOSBY_CRED_NAME, Time.Year, Time.Month, Time.Day);
500503
List.Entry[i].Description = DbSubject;
501-
Status = GenerateCredentials(DbSubject, &Cred);
504+
Status = GenerateCredentials(DbSubject, &DbCred);
502505
if (EFI_ERROR(Status))
503506
goto exit;
504-
Status = CertToAuthVar(Cred.Cert, &List.Entry[i].Variable, FALSE);
505-
if (EFI_ERROR(Status)) {
506-
FreeCredentials(&Cred);
507+
Status = CertToAuthVar(DbCred.Cert, &List.Entry[i].Variable, FALSE);
508+
if (EFI_ERROR(Status))
507509
goto exit;
508-
}
509-
List.Entry[i].Attrs = UEFI_VAR_NV_BS_RT_AT_AP;
510-
Status = SaveCredentials(WIDEN(MOSBY_CRED_NAME), &Cred);
510+
Status = SaveCredentials(WIDEN(MOSBY_CRED_NAME), &DbCred);
511511
if (EFI_ERROR(Status))
512512
goto exit;
513513
RecallPrint(L"Saved Secure Boot signing credentials as '%a'\n", MOSBY_CRED_NAME);
514-
FreeCredentials(&Cred);
514+
515+
List.Entry[i].Attrs = UEFI_VAR_NV_BS_RT_AT_AP;
516+
Status = SignAuthVar(KeyInfo[DB].VariableName, KeyInfo[DB].VariableGuid,
517+
List.Entry[i].Attrs, &List.Entry[i].Variable, &PkCred);
518+
if (EFI_ERROR(Status))
519+
ReportErrorAndExit(L"Failed to sign DB\n");
515520
List.Size++;
516521
}
517522

518-
/* Generate a keyless PK cert if none was specified */
523+
/* Set up the PK if none was specified */
519524
LastEntry = RemoveDuplicates(PK, &List);
520525
if (LastEntry < 0) {
521526
if (List.Size >= MOSBY_MAX_LIST_SIZE)
522527
Abort(EFI_OUT_OF_RESOURCES, L"List size is too small\n");
523528
RecallPrint(L"Generating PK certificate...\n");
524529
i = List.Size;
525530
List.Entry[i].Type = PK;
526-
if (!GenDBCred) {
527-
Status = gRT->GetTime(&Time, NULL);
528-
if (EFI_ERROR(Status))
529-
ReportErrorAndExit(L"Failed to get time - Aborting\n");
530-
}
531-
AsciiSPrint(PkSubject, sizeof(PkSubject), "Mosby Generated PK [%04d.%02d.%02d]",
532-
Time.Year, Time.Month, Time.Day);
533531
List.Entry[i].Description = PkSubject;
534-
Status = GenerateCredentials(PkSubject, &Cred);
532+
Status = CertToAuthVar(PkCred.Cert, &List.Entry[i].Variable, FALSE);
535533
if (EFI_ERROR(Status))
536534
goto exit;
537-
Status = CertToAuthVar(Cred.Cert, &List.Entry[i].Variable, FALSE);
538-
if (EFI_ERROR(Status)) {
539-
FreeCredentials(&Cred);
540-
goto exit;
541-
}
542535
// PK must be signed
543536
List.Entry[i].Attrs = UEFI_VAR_NV_BS_RT_AT;
544-
Status = SignToAuthVar(KeyInfo[PK].VariableName, KeyInfo[PK].VariableGuid,
545-
List.Entry[i].Attrs, &List.Entry[i].Variable, &Cred);
546-
FreeCredentials(&Cred);
537+
Status = SignAuthVar(KeyInfo[PK].VariableName, KeyInfo[PK].VariableGuid,
538+
List.Entry[i].Attrs, &List.Entry[i].Variable, &PkCred);
547539
if (EFI_ERROR(Status)) {
548540
SafeFree(List.Entry[i].Variable.Data);
549541
goto exit;
@@ -554,9 +546,10 @@ EFI_STATUS EFIAPI efi_main(
554546
#if defined(_M_X64) || defined(__x86_64__) || defined(_M_IX86) || defined(__i386__)
555547
/*
556548
* There appears to be a whole sway of AMI UEFI firmwares with a rather unfortunate bug,
557-
* that prevents appending to an existing KEK store. Which means that, on the affected
549+
* that prevents appending to an existing KEK store (or even creating the initial KEK
550+
* variable if it is done with the append flag set). Which means that, on the affected
558551
* systems, if we try to write more than one KEK, using multiple SetVariable() calls,
559-
* only the first call succeeds and all subsequent ones fail with EFI_INVALID_PARAMETER.
552+
* only the first can succeed and all subsequent ones fail with EFI_INVALID_PARAMETER.
560553
* To work around this and since any unsigned KEK we processed above should already have
561554
* been converted to an ESL (embedded in an EFI_VARIABLE_AUTHENTICATION_2 struct), we
562555
* concatenate all these ESLs into a single array, which can then be written through a
@@ -581,8 +574,9 @@ EFI_STATUS EFIAPI efi_main(
581574
RecallPrint(L"Adding '%s' to Merged KEK List\n", List.Entry[i].Path);
582575
/* Get the ESL data from EFI_VARIABLE_AUTHENTICATION_2 (at .AuthInfo.CertData) */
583576
Esl[EslIndex++] = (EFI_SIGNATURE_LIST*)&((UINT8*)List.Entry[i].Variable.Data)[
577+
((List.Entry[i].Variable.Data->AuthInfo.CertData[2] << 8) | List.Entry[i].Variable.Data->AuthInfo.CertData[3]) +
584578
OFFSET_OF(EFI_VARIABLE_AUTHENTICATION_2, AuthInfo) +
585-
OFFSET_OF(WIN_CERTIFICATE_UEFI_GUID, CertData)];
579+
OFFSET_OF(WIN_CERTIFICATE_UEFI_GUID, CertData) + 4];
586580
/* Remove the individual KEK from our installation list, since it will be merged */
587581
List.Entry[i].Flags |= NO_INSTALL;
588582
}
@@ -604,7 +598,7 @@ EFI_STATUS EFIAPI efi_main(
604598
List.Entry[List.Size].Attrs = UEFI_VAR_NV_BS_RT_AT;
605599
List.Entry[List.Size].Description = "Merged KEK List";
606600
List.Entry[List.Size].Path = L"Merged KEK List";
607-
Status = PopulateAuthVar(&List.Entry[List.Size]);
601+
Status = PopulateAuthVar(&List.Entry[List.Size], &PkCred);
608602
if (EFI_ERROR(Status))
609603
ReportErrorAndExit(L"Failed to create merged KEK variable - Aborting\n");
610604
List.Size++;
@@ -644,6 +638,8 @@ EFI_STATUS EFIAPI efi_main(
644638
exit:
645639
for (i = 0; i < List.Size; i++)
646640
FreePool(List.Entry[i].Variable.Data);
641+
FreeCredentials(&DbCred);
642+
FreeCredentials(&PkCred);
647643
FreePool(Argv);
648644
RecallPrintFree();
649645
CloseLogger();

src/mosby.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,14 @@ enum {
114114
MAX_TYPES
115115
};
116116

117+
/* Attributes for the "key" types we support */
118+
typedef struct {
119+
CHAR8 *DisplayName;
120+
CHAR16 *OptionName;
121+
CHAR16 *VariableName;
122+
EFI_GUID *VariableGuid;
123+
} MOSBY_KEY_INFO;
124+
117125
/* Mosby buffer struct */
118126
typedef struct {
119127
UINTN Size;

src/pki.c

Lines changed: 20 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,8 @@
5050
ERR_print_errors_cb(OpenSSLErrorCallback, _ErrMsg); goto exit; \
5151
} while(0)
5252

53+
extern MOSBY_KEY_INFO KeyInfo[MAX_TYPES];
54+
5355
STATIC EFI_TIME mTime = { 0 };
5456

5557
/* For OpenSSL error reporting */
@@ -406,15 +408,16 @@ EFI_STATUS CertToAuthVar(
406408
}
407409

408410
EFI_STATUS PopulateAuthVar(
409-
IN OUT MOSBY_ENTRY *Entry
411+
IN OUT MOSBY_ENTRY *Entry,
412+
IN MOSBY_CRED *Credentials
410413
)
411414
{
412415
EFI_STATUS Status = EFI_INVALID_PARAMETER;
413416
UINTN HeaderSize;
414417
CONST UINT8 *Ptr;
415418
EFI_SIGNATURE_LIST *Esl = NULL;
416419
MOSBY_CRED Cred = { 0 };
417-
EFI_VARIABLE_AUTHENTICATION_2 *SignedEsl = NULL;
420+
EFI_VARIABLE_AUTHENTICATION_2 *AuthVar = NULL;
418421
PKCS12 *p12 = NULL;
419422
BIO *bio = NULL;
420423

@@ -429,15 +432,15 @@ EFI_STATUS PopulateAuthVar(
429432
Entry->Attrs = (Entry->Type == MOK) ? UEFI_VAR_NV_BS_AP : UEFI_VAR_NV_BS_RT_AT_AP;
430433

431434
// Check for signed ESL (PKCS#7 only)
432-
SignedEsl = (EFI_VARIABLE_AUTHENTICATION_2*)Entry->Buffer.Data;
435+
AuthVar = (EFI_VARIABLE_AUTHENTICATION_2*)Entry->Buffer.Data;
433436
if (Entry->Buffer.Size > sizeof(EFI_VARIABLE_AUTHENTICATION_2) &&
434-
SignedEsl->AuthInfo.Hdr.dwLength < Entry->Buffer.Size &&
435-
SignedEsl->AuthInfo.Hdr.wRevision == 0x0200 &&
436-
SignedEsl->AuthInfo.Hdr.wCertificateType == WIN_CERT_TYPE_EFI_GUID &&
437-
CompareGuid(&SignedEsl->AuthInfo.CertType, &gEfiCertPkcs7Guid)) {
438-
if (SignedEsl->AuthInfo.CertData[0] != 0x30 || SignedEsl->AuthInfo.CertData[1] != 0x82)
437+
AuthVar->AuthInfo.Hdr.dwLength < Entry->Buffer.Size &&
438+
AuthVar->AuthInfo.Hdr.wRevision == 0x0200 &&
439+
AuthVar->AuthInfo.Hdr.wCertificateType == WIN_CERT_TYPE_EFI_GUID &&
440+
CompareGuid(&AuthVar->AuthInfo.CertType, &gEfiCertPkcs7Guid)) {
441+
if (AuthVar->AuthInfo.CertData[0] != 0x30 || AuthVar->AuthInfo.CertData[1] != 0x82)
439442
ReportErrorAndExit(L"Invalid signed ESL '%s'\n", Entry->Path);
440-
HeaderSize = (SignedEsl->AuthInfo.CertData[2] << 8) | SignedEsl->AuthInfo.CertData[3];
443+
HeaderSize = (AuthVar->AuthInfo.CertData[2] << 8) | AuthVar->AuthInfo.CertData[3];
441444
HeaderSize += OFFSET_OF(EFI_VARIABLE_AUTHENTICATION_2, AuthInfo);
442445
HeaderSize += OFFSET_OF(WIN_CERTIFICATE_UEFI_GUID, CertData);
443446
HeaderSize += 4; // For the 4 extra bytes above
@@ -454,11 +457,11 @@ EFI_STATUS PopulateAuthVar(
454457
if ((UINTN)Esl != (UINTN)&Entry->Buffer.Data[Entry->Buffer.Size])
455458
ReportErrorAndExit(L"Invalid signed ESL '%s'\n", Entry->Path);
456459
Entry->Variable.Size = Entry->Buffer.Size;
457-
Entry->Variable.Data = SignedEsl;
460+
Entry->Variable.Data = AuthVar;
458461
Entry->Flags |= ALLOW_UPDATE;
459462
Entry->Buffer.Data = NULL; // Don't double free our data
460-
Status = EFI_SUCCESS;
461-
goto exit;
463+
// TODO: Do we want to validate the signature too?
464+
return EFI_SUCCESS;
462465
}
463466

464467
// Check for a DER encoded X509 certificate
@@ -518,11 +521,14 @@ EFI_STATUS PopulateAuthVar(
518521
Entry->Attrs = 0;
519522
Entry->Variable.Size = 0;
520523
SafeFree(Entry->Variable.Data);
524+
return Status;
521525
}
522-
return Status;
526+
// Sign the authvar
527+
return SignAuthVar(KeyInfo[Entry->Type].VariableName, KeyInfo[Entry->Type].VariableGuid,
528+
Entry->Attrs, &Entry->Variable, Credentials);
523529
}
524530

525-
EFI_STATUS SignToAuthVar(
531+
EFI_STATUS SignAuthVar(
526532
IN CONST CHAR16 *VariableName,
527533
IN CONST EFI_GUID *VariableGuid,
528534
IN CONST UINT32 Attributes,

src/pki.h

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
/*
22
* MSSB (More Secure Secure Boot -- "Mosby") PKI/OpenSSL functions
3-
* Copyright 2024-2025 Pete Batard <pete@akeo.ie>
3+
* Copyright 2024-2026 Pete Batard <pete@akeo.ie>
44
*
55
* This program is free software: you can redistribute it and/or modify
66
* it under the terms of the GNU General Public License as published by
@@ -51,10 +51,11 @@ EFI_STATUS CertToAuthVar(
5151
);
5252

5353
EFI_STATUS PopulateAuthVar(
54-
IN OUT MOSBY_ENTRY *Entry
54+
IN OUT MOSBY_ENTRY *Entry,
55+
IN MOSBY_CRED *Credentials
5556
);
5657

57-
EFI_STATUS SignToAuthVar(
58+
EFI_STATUS SignAuthVar(
5859
IN CONST CHAR16 *VariableName,
5960
IN CONST EFI_GUID *VendorGuid,
6061
IN CONST UINT32 Attributes,

0 commit comments

Comments
 (0)