sd-boot: add support for custom mode.

Custom mode allows to write updates to db, dbx, KEK and PK without
signature.  See the comment block for a more detailed description.

In case the PK update has no signature try to enable custom mode.
This commit is contained in:
Gerd Hoffmann 2024-03-18 09:23:16 +01:00 committed by Luca Boccassi
parent 3037616d8e
commit 102138a12e
2 changed files with 62 additions and 0 deletions

View file

@ -140,6 +140,8 @@ typedef struct {
GUID_DEF(0x8be4df61, 0x93ca, 0x11d2, 0xaa, 0x0d, 0x00, 0xe0, 0x98, 0x03, 0x2b, 0x8c)
#define EFI_IMAGE_SECURITY_DATABASE_GUID \
GUID_DEF(0xd719b2cb, 0x3d3a, 0x4596, 0xa3, 0xbc, 0xda, 0xd0, 0x0e, 0x67, 0x65, 0x6f)
#define EFI_CUSTOM_MODE_ENABLE_GUID \
GUID_DEF(0xc076ec0c, 0x7028, 0x4399, 0xa0, 0x72, 0x71, 0xee, 0x5c, 0x44, 0x8b, 0x9f)
#define EVT_TIMER 0x80000000U
#define EVT_RUNTIME 0x40000000U

View file

@ -32,10 +32,46 @@ SecureBootMode secure_boot_mode(void) {
return decode_secure_boot_mode(secure, audit, deployed, setup);
}
/*
* Custom mode allows to change secure boot certificate databases db, dbx, KEK and PK without the variable
* updates being signed. When enrolling certificates to an unconfigured system (no PK present yet) writing
* db, dbx and KEK updates without signature works fine even in standard mode. Writing PK updates without
* signature requires custom mode in any case.
*
* Enabling custom mode works only if a user is physically present. Note that OVMF has a dummy
* implementation for the user presence check (there is no useful way to implement a presence check for a
* virtual machine).
*
* FYI: Your firmware setup utility might offers the option to enroll certificates from *.crt files
* (DER-encoded x509 certificates) on the ESP; that uses custom mode too. Your firmware setup might also
* offer the option to switch the system into custom mode for the next boot.
*/
static bool custom_mode_enabled(void) {
bool enabled = false;
(void) efivar_get_boolean_u8(MAKE_GUID_PTR(EFI_CUSTOM_MODE_ENABLE),
u"CustomMode", &enabled);
return enabled;
}
static EFI_STATUS set_custom_mode(bool enable) {
static char16_t name[] = u"CustomMode";
static uint32_t attr =
EFI_VARIABLE_NON_VOLATILE |
EFI_VARIABLE_BOOTSERVICE_ACCESS;
uint8_t mode = enable
? 1 /* CUSTOM_SECURE_BOOT_MODE */
: 0; /* STANDARD_SECURE_BOOT_MODE */
return RT->SetVariable(name, MAKE_GUID_PTR(EFI_CUSTOM_MODE_ENABLE),
attr, sizeof(mode), &mode);
}
EFI_STATUS secure_boot_enroll_at(EFI_FILE *root_dir, const char16_t *path, bool force) {
assert(root_dir);
assert(path);
bool need_custom_mode = false;
EFI_STATUS err;
clear_screen(COLOR_NORMAL);
@ -103,6 +139,30 @@ EFI_STATUS secure_boot_enroll_at(EFI_FILE *root_dir, const char16_t *path, bool
log_error_status(err, "Failed reading file %ls\\%ls: %m", path, sb_vars[i].filename);
goto out_deallocate;
}
if (streq16(sb_vars[i].name, u"PK") && sb_vars[i].size > 20) {
assert(sb_vars[i].buffer);
/*
* The buffer should be EFI_TIME (16 bytes), followed by
* EFI_VARIABLE_AUTHENTICATION_2 header. First header field is the size. If the
* size covers only the header itself (8 bytes) plus the signature type guid (16
* bytes), leaving no space for an actual signature, we can conclude that no
* signature is present.
*/
uint32_t *sigsize = (uint32_t*)(sb_vars[i].buffer + 16);
if (*sigsize <= 24) {
printf("PK is not signed (need custom mode).\n");
need_custom_mode = true;
}
}
}
if (need_custom_mode && !custom_mode_enabled()) {
err = set_custom_mode(/* enable */ true);
if (err != EFI_SUCCESS) {
log_error_status(err, "Failed to enable custom mode: %m");
goto out_deallocate;
}
printf("Custom mode enabled.\n");
}
for (size_t i = 0; i < ELEMENTSOF(sb_vars); i++) {