sd-boot: add support for custom mode.
authorGerd Hoffmann <kraxel@redhat.com>
Mon, 18 Mar 2024 08:23:16 +0000 (09:23 +0100)
committerLuca Boccassi <luca.boccassi@gmail.com>
Wed, 20 Mar 2024 11:04:14 +0000 (11:04 +0000)
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.

src/boot/efi/efi.h
src/boot/efi/secure-boot.c

index fbc5d105653cb61eb54936c4c5ec85a52a84a2b6..e8217c1836102750f3b7776e46a0f852e1df7a40 100644 (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
index f76d2f46d75ac907d44c60bd209a92ff628a8ddc..155ff68cd6297a3c812b49016c9800dfd7b385a1 100644 (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++) {