misc: Enforce hash verification when UEFI Secure Boot is active
diff --git a/CONFIG.md b/CONFIG.md
index 23fbe3b8..b6bac469 100644
--- a/CONFIG.md
+++ b/CONFIG.md
@@ -115,7 +115,8 @@ Miscellaneous:
Limine, this will only randomise memory below 4GiB.
* `randomize_memory` - Alias of `randomise_memory`.
* `hash_mismatch_panic` - If set to `no`, do not panic if there is a hash
- mismatch for a file, but print a warning instead.
+ mismatch for a file, but print a warning instead. Forced to `yes` when
+ Secure Boot is active.
Limine interface control options:
@@ -195,7 +196,8 @@ These are ignored if using text mode.
Editor control options:
* `editor_enabled` - If set to `no`, the editor will not be accessible.
- Defaults to `yes` unless a config hash is enrolled.
+ Defaults to `yes` unless a config hash is enrolled. Unconditionally
+ disabled when Secure Boot is active.
* `editor_highlighting` - If set to `no`, syntax highlighting in the editor
will be disabled. Defaults to `yes`.
* `editor_validation` - If set to `no`, the editor will not alert you about
@@ -342,6 +344,8 @@ A resource can be one of the following:
A path can optionally be suffixed with a blake2b hash for the referenced file,
by appending a pound character (`#`) followed by the blake2b hash.
E.g.: `boot():/somemodule.tar#ca6914d2...446b470a`.
+When Secure Boot is active, all file paths **must** have a hash appended or
+Limine will panic (except for wallpapers, which are silently skipped instead).
## Macros
diff --git a/FAQ.md b/FAQ.md
index 15160783..f478a226 100644
--- a/FAQ.md
+++ b/FAQ.md
@@ -27,6 +27,11 @@ checksum of the config file itself. The EFI executable can then get signed with
a key added to the firmware's keychain. This prevents modifications to the
config file (and in turn the checksums contained there) from going unnoticed.
+Additionally, when Limine detects that UEFI Secure Boot is active, it enforces
+that a config checksum is enrolled, that all loaded files have BLAKE2B hashes
+in their paths, and disables the config editor. See [USAGE.md](USAGE.md) for
+details.
+
### I do not want to have a separate FAT boot partition! What can I do?
It is `$year_following_2012` now and most PCs are equipped with UEFI and simply
diff --git a/USAGE.md b/USAGE.md
index f7917756..e1f64ca7 100644
--- a/USAGE.md
+++ b/USAGE.md
@@ -14,13 +14,26 @@ A valid config file should also be provided as described in
[CONFIG.md](CONFIG.md).
## Secure Boot
-Limine can be booted with secure boot if the executable is signed and the key
+Limine can be booted with Secure Boot if the executable is signed and the key
used to sign it is added to the firmware's keychain. This should be done in
combination with enrolling the BLAKE2B hash of the Limine config file into the
Limine EFI executable image itself for verification purposes.
For more information see the `limine enroll-config` program and
[the FAQ](FAQ.md).
+When Limine detects that UEFI Secure Boot is active (the `SecureBoot` variable
+is set and `SetupMode` is not), the following security policies are enforced:
+
+* The config file **must** have a BLAKE2B checksum enrolled in the Limine EFI
+ executable. If no checksum is enrolled, Limine will panic.
+* All file paths (kernels, modules, DTBs, fonts, etc.) **must** have a BLAKE2B
+ hash appended (e.g. `boot():/kernel#<hash>`). Loading a file without a hash
+ will cause a panic.
+* Wallpaper files without an associated hash are silently skipped rather than
+ causing a panic.
+* The config editor is unconditionally disabled.
+* `hash_mismatch_panic` is forced to `yes` regardless of the config setting.
+
## BIOS/MBR
In order to install Limine on a MBR device (which can just be a raw image
file), run `limine bios-install` as such:
diff --git a/common/entry.s3.c b/common/entry.s3.c
index 7e78d5e0..0b45967f 100644
--- a/common/entry.s3.c
+++ b/common/entry.s3.c
@@ -91,6 +91,22 @@ defer_error:
disk_create_index();
+ // Detect UEFI Secure Boot
+ {
+ EFI_GUID global_variable = EFI_GLOBAL_VARIABLE;
+ UINT8 secure_boot = 0;
+ UINTN sb_size = sizeof(secure_boot);
+ EFI_STATUS sb_status = gRT->GetVariable(L"SecureBoot", &global_variable, NULL, &sb_size, &secure_boot);
+ if (sb_status == EFI_SUCCESS && secure_boot == 1) {
+ UINT8 setup_mode = 0;
+ UINTN sm_size = sizeof(setup_mode);
+ EFI_STATUS sm_status = gRT->GetVariable(L"SetupMode", &global_variable, NULL, &sm_size, &setup_mode);
+ if (sm_status != EFI_SUCCESS || setup_mode == 0) {
+ secure_boot_active = true;
+ }
+ }
+ }
+
boot_volume = NULL;
EFI_HANDLE current_handle = ImageHandle;
diff --git a/common/lib/config.c b/common/lib/config.c
index 3984bdb8..26bbf895 100644
--- a/common/lib/config.c
+++ b/common/lib/config.c
@@ -353,6 +353,10 @@ static struct macro *macros = NULL;
int init_config(size_t config_size) {
config_b2sum += sizeof(CONFIG_B2SUM_SIGNATURE) - 1;
+ if (secure_boot_active && memcmp((void *)config_b2sum, CONFIG_B2SUM_EMPTY, 128) == 0) {
+ panic(false, "!!! SECURE BOOT IS ACTIVE BUT NO CONFIG CHECKSUM IS ENROLLED !!!");
+ }
+
if (memcmp((void *)config_b2sum, CONFIG_B2SUM_EMPTY, 128) != 0) {
editor_enabled = false;
diff --git a/common/lib/gterm.c b/common/lib/gterm.c
index 41734117..99686dce 100644
--- a/common/lib/gterm.c
+++ b/common/lib/gterm.c
@@ -584,10 +584,14 @@ static void gterm_parse_config(char *config, struct gterm_config *cfg) {
if (wallpaper_count > 0) {
char *background_path = config_get_value(config, rand32() % wallpaper_count, "WALLPAPER");
if (background_path != NULL) {
- struct file_handle *bg_file;
- if ((bg_file = uri_open(background_path)) != NULL) {
- background = image_open(bg_file);
- fclose(bg_file);
+ if (secure_boot_active && strchr(background_path, '#') == NULL) {
+ print("Wallpaper skipped: Secure Boot is active and no hash is associated.\n");
+ } else {
+ struct file_handle *bg_file;
+ if ((bg_file = uri_open(background_path)) != NULL) {
+ background = image_open(bg_file);
+ fclose(bg_file);
+ }
}
}
}
diff --git a/common/lib/misc.c b/common/lib/misc.c
index 408df43a..45cfdbb8 100644
--- a/common/lib/misc.c
+++ b/common/lib/misc.c
@@ -25,6 +25,7 @@ UINT32 efi_desc_ver = 0;
bool editor_enabled = true;
bool help_hidden = false;
+bool secure_boot_active = false;
uint64_t usec_at_bootloader_entry;
diff --git a/common/lib/misc.h b/common/lib/misc.h
index 1e8286c5..60f709ef 100644
--- a/common/lib/misc.h
+++ b/common/lib/misc.h
@@ -38,7 +38,7 @@ extern struct volume *boot_volume;
extern bool stage3_loaded;
#endif
-extern bool quiet, serial, editor_enabled, help_hidden, hash_mismatch_panic;
+extern bool quiet, serial, editor_enabled, help_hidden, hash_mismatch_panic, secure_boot_active;
extern uint64_t usec_at_bootloader_entry;
diff --git a/common/lib/uri.c b/common/lib/uri.c
index f390d2cb..7f868791 100644
--- a/common/lib/uri.c
+++ b/common/lib/uri.c
@@ -252,6 +252,10 @@ struct file_handle *uri_open(char *uri) {
panic(true, "Resource `%s` not valid.", resource);
}
+ if (secure_boot_active && hash == NULL && ret != NULL) {
+ panic(true, "Secure Boot is active and URI `%#` has no associated hash!", uri);
+ }
+
if (hash != NULL && ret != NULL) {
uint8_t out_buf[BLAKE2B_OUT_BYTES];
#if defined (UEFI) && defined (__x86_64__)
diff --git a/common/menu.c b/common/menu.c
index 5c388bb4..24036220 100644
--- a/common/menu.c
+++ b/common/menu.c
@@ -1030,6 +1030,11 @@ noreturn void _menu(bool first_run) {
char *hash_mismatch_panic_str = config_get_value(NULL, 0, "HASH_MISMATCH_PANIC");
hash_mismatch_panic = hash_mismatch_panic_str == NULL || strcmp(hash_mismatch_panic_str, "yes") == 0;
+ if (secure_boot_active) {
+ hash_mismatch_panic = true;
+ editor_enabled = false;
+ }
+
char *randomise_mem_str = config_get_value(NULL, 0, "RANDOMISE_MEMORY");
if (randomise_mem_str == NULL)
randomise_mem_str = config_get_value(NULL, 0, "RANDOMIZE_MEMORY");
@@ -1039,7 +1044,7 @@ noreturn void _menu(bool first_run) {
}
char *editor_enabled_str = config_get_value(NULL, 0, "EDITOR_ENABLED");
- if (editor_enabled_str != NULL) {
+ if (editor_enabled_str != NULL && !secure_boot_active) {
editor_enabled = strcmp(editor_enabled_str, "yes") == 0;
}
@@ -1070,9 +1075,9 @@ noreturn void _menu(bool first_run) {
{
uint32_t eax, ebx, ecx, edx;
if (!cpuid(0x80000001, 0, &eax, &ebx, &ecx, &edx) || !(edx & (1 << 29))) {
- menu_branding = "Limine " LIMINE_VERSION " (ia-32, BIOS)";
+ menu_branding = strdup("Limine " LIMINE_VERSION " (ia-32, BIOS)");
} else {
- menu_branding = "Limine " LIMINE_VERSION " (x86-64, BIOS)";
+ menu_branding = strdup("Limine " LIMINE_VERSION " (x86-64, BIOS)");
}
}
#elif defined (UEFI)
@@ -1080,13 +1085,13 @@ noreturn void _menu(bool first_run) {
{
uint32_t eax, ebx, ecx, edx;
if (!cpuid(0x80000001, 0, &eax, &ebx, &ecx, &edx) || !(edx & (1 << 29))) {
- menu_branding = "Limine " LIMINE_VERSION " (ia-32, UEFI32)";
+ menu_branding = strdup("Limine " LIMINE_VERSION " (ia-32, UEFI32)");
} else {
- menu_branding = "Limine " LIMINE_VERSION " (x86-64, UEFI32)";
+ menu_branding = strdup("Limine " LIMINE_VERSION " (x86-64, UEFI32)");
}
}
#else
- menu_branding = "Limine " LIMINE_VERSION " ("
+ menu_branding = strdup("Limine " LIMINE_VERSION " ("
#if defined (__x86_64__)
"x86-64"
#elif defined (__riscv)
@@ -1096,11 +1101,19 @@ noreturn void _menu(bool first_run) {
#elif defined (__loongarch64)
"loongarch64"
#endif
- ", UEFI)";
+ ", UEFI)");
#endif
#endif
}
+ if (secure_boot_active) {
+ const char *suffix = ", Secure Boot)";
+ size_t suffix_len = strlen(suffix) + 1;
+ size_t old_len = strlen(menu_branding);
+ menu_branding = pmm_realloc(menu_branding, old_len + 1, old_len + suffix_len);
+ memcpy(menu_branding + old_len - 1, suffix, suffix_len);
+ }
+
{
char *tmp = config_get_value(NULL, 0, "INTERFACE_BRANDING_COLOUR");
if (tmp == NULL)
