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
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);
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++) {