Message ID | 148120024570.5854.10638278395097394138.stgit@warthog.procyon.org.uk (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
On Thu, 08 Dec, at 12:30:45PM, David Howells wrote: > Get the firmware's secure-boot status in the kernel boot wrapper and stash > it somewhere that the main kernel image can find. > > The efi_get_secureboot() function is extracted from the arm stub and (a) > generalised so that it can be called from x86 and (b) made to use > efi_call_runtime() so that it can be run in mixed-mode. > > Suggested-by: Lukas Wunner <lukas@wunner.de> > Signed-off-by: David Howells <dhowells@redhat.com> > --- > > Documentation/x86/zero-page.txt | 2 + > arch/x86/boot/compressed/eboot.c | 2 + > arch/x86/boot/compressed/head_32.S | 1 > arch/x86/boot/compressed/head_64.S | 1 > arch/x86/include/asm/bootparam_utils.h | 5 +- > arch/x86/include/uapi/asm/bootparam.h | 3 + > arch/x86/kernel/asm-offsets.c | 1 > drivers/firmware/efi/libstub/Makefile | 2 - > drivers/firmware/efi/libstub/arm-stub.c | 63 +++-------------------------- > drivers/firmware/efi/libstub/secureboot.c | 63 +++++++++++++++++++++++++++++ > include/linux/efi.h | 8 ++++ > 11 files changed, 90 insertions(+), 61 deletions(-) > create mode 100644 drivers/firmware/efi/libstub/secureboot.c [...] > diff --git a/arch/x86/boot/compressed/head_32.S b/arch/x86/boot/compressed/head_32.S > index d85b9625e836..c635f7e32f5c 100644 > --- a/arch/x86/boot/compressed/head_32.S > +++ b/arch/x86/boot/compressed/head_32.S > @@ -61,6 +61,7 @@ > > __HEAD > ENTRY(startup_32) > + movb $0, BP_secure_boot(%esi) > #ifdef CONFIG_EFI_STUB > jmp preferred_addr > > diff --git a/arch/x86/boot/compressed/head_64.S b/arch/x86/boot/compressed/head_64.S > index beab8322f72a..ccd2c7461b7f 100644 > --- a/arch/x86/boot/compressed/head_64.S > +++ b/arch/x86/boot/compressed/head_64.S > @@ -244,6 +244,7 @@ ENTRY(startup_64) > * that maps our entire kernel(text+data+bss+brk), zero page > * and command line. > */ > + movb $0, BP_secure_boot(%rsi) > #ifdef CONFIG_EFI_STUB > /* > * The entry point for the PE/COFF executable is efi_pe_entry, so Is clearing ::secure_boot really necessary? Any code path that goes via efi_main() will set it correctly and all other code paths should get it cleared in sanitize_boot_params(), no? > diff --git a/include/linux/efi.h b/include/linux/efi.h > index c7904556d7a8..92e23f03045e 100644 > --- a/include/linux/efi.h > +++ b/include/linux/efi.h > @@ -1477,6 +1477,14 @@ efi_status_t efi_setup_gop(efi_system_table_t *sys_table_arg, > bool efi_runtime_disabled(void); > extern void efi_call_virt_check_flags(unsigned long flags, const char *call); > > +enum efi_secureboot_mode { > + efi_secureboot_mode_unset, > + efi_secureboot_mode_unknown, > + efi_secureboot_mode_disabled, > + efi_secureboot_mode_enabled, > +}; > +enum efi_secureboot_mode efi_get_secureboot(efi_system_table_t *sys_table); > + > /* > * Arch code can implement the following three template macros, avoiding > * reptition for the void/non-void return cases of {__,}efi_call_virt(): > What's the distinction between the unset and unknown enums? -- To unsubscribe from this list: send the line "unsubscribe linux-security-module" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Matt Fleming <matt@codeblueprint.co.uk> wrote: > > + movb $0, BP_secure_boot(%rsi) > > #ifdef CONFIG_EFI_STUB > > /* > > * The entry point for the PE/COFF executable is efi_pe_entry, so > > Is clearing ::secure_boot really necessary? Any code path that goes > via efi_main() will set it correctly and all other code paths should > get it cleared in sanitize_boot_params(), no? No. The boot_params->secure_boot parameter exists whether or not efi_main() is traversed (ie. if EFI isn't enabled or CONFIG_EFI_STUB=n) and, if not cleared, is of uncertain value. Further, sanitize_boot_params() has to be modified by this patch so as not to clobber the secure_boot flag. > What's the distinction between the unset and unknown enums? unset -> The flag was cleared by head.S and efi_get_secureboot() was never called. unknown -> efi_get_secureboot() tried and failed to access the EFI variables that should give the state. David -- To unsubscribe from this list: send the line "unsubscribe linux-security-module" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
(Cc'ing Peter A. and Peter J. for boot params discussion) On Wed, 11 Jan, at 03:27:23PM, David Howells wrote: > Matt Fleming <matt@codeblueprint.co.uk> wrote: > > > > + movb $0, BP_secure_boot(%rsi) > > > #ifdef CONFIG_EFI_STUB > > > /* > > > * The entry point for the PE/COFF executable is efi_pe_entry, so > > > > Is clearing ::secure_boot really necessary? Any code path that goes > > via efi_main() will set it correctly and all other code paths should > > get it cleared in sanitize_boot_params(), no? > > No. > > The boot_params->secure_boot parameter exists whether or not efi_main() is > traversed (ie. if EFI isn't enabled or CONFIG_EFI_STUB=n) and, if not cleared, > is of uncertain value. > > Further, sanitize_boot_params() has to be modified by this patch so as not to > clobber the secure_boot flag. Any new parameters that boot loaders do not know about should be cleared to zero by default in the boot loader because boot_params itself should be zero'd when allocated. There are two cases to consider: 1) boot_params is not zero'd 2) boot_params is zero'd 1) This is a broken boot loader implementation that violates the x86 boot specification and I would never expect ->secure_boot to have a valid value. It should not be special-cased in sanitize_boot_params(), it should be zero'd. 2) In this case ->secure_boot should be zero unless modified inside of efi_main(). Did you hit the scenario where ->secure_boot has a garbage value while developing these patches? I wouldn't expect to see it in practice. -- To unsubscribe from this list: send the line "unsubscribe linux-security-module" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Matt Fleming <matt@codeblueprint.co.uk> wrote: > On Wed, 11 Jan, at 03:27:23PM, David Howells wrote: > > Matt Fleming <matt@codeblueprint.co.uk> wrote: > > > > > > + movb $0, BP_secure_boot(%rsi) > > > > #ifdef CONFIG_EFI_STUB > > > > /* > > > > * The entry point for the PE/COFF executable is efi_pe_entry, so > > > > > > Is clearing ::secure_boot really necessary? Any code path that goes > > > via efi_main() will set it correctly and all other code paths should > > > get it cleared in sanitize_boot_params(), no? > > > > No. > > > > The boot_params->secure_boot parameter exists whether or not efi_main() is > > traversed (ie. if EFI isn't enabled or CONFIG_EFI_STUB=n) and, if not cleared, > > is of uncertain value. > > > > Further, sanitize_boot_params() has to be modified by this patch so as not to > > clobber the secure_boot flag. > > Any new parameters that boot loaders do not know about should be > cleared to zero by default in the boot loader because boot_params > itself should be zero'd when allocated. Do you mean the boot loader or the boot wrapper? If the loader, that is outside my control - and given the purpose of the value, I'm not sure I want to rely on that. > There are two cases to consider: > > 1) boot_params is not zero'd > 2) boot_params is zero'd > > 1) This is a broken boot loader implementation that violates the x86 > boot specification and I would never expect ->secure_boot to have a > valid value. If there's a boot specification that must be complied with, why does sanitize_boot_params() even exist? Why does the comment on it say: * Deal with bootloaders which fail to initialize unknown fields in * boot_params to zero. The list fields in this list are taken from * analysis of kexec-tools; if other broken bootloaders initialize a * different set of fields we will need to figure out how to disambiguate. > It should not be special-cased in sanitize_boot_params(), it should be > zero'd. Sigh. sanitize_boot_params() is part of the problem. The startup sequence goes something like this: (0) We enter the boot wrapper. (1) We clear the secure-boot status value [my patch adds this]. (2) The boot wrapper *may* invoke efi_main() - which will determine the secure-boot status. (3) The boot wrapper calls extract_kernel() to decompress the kernel. (4) extract_kernel() calls sanitize_boot_params() which would otherwise clear the secure-boot flag. (5) The boot wrapper jumps into the main kernel image, which now does not see the secure boot status value we calculated. So, no, sanitize_boot_params() must *not* zero the value unless we change the call point for s_b_p(). > 2) In this case ->secure_boot should be zero unless modified inside of > efi_main(). I have no idea whether this is guaranteed or not. > Did you hit the scenario where ->secure_boot has a garbage value while > developing these patches? I wouldn't expect to see it in practice. I haven't actually checked what the value was before I cleared it. But, I've found that security people get seriously paranoid about assuming things to be implicitly so;-). David -- To unsubscribe from this list: send the line "unsubscribe linux-security-module" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Hi Matt, Ard, Any further thoughts? Thanks, David -- To unsubscribe from this list: send the line "unsubscribe linux-security-module" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
On Mon, 16 Jan, at 03:39:18PM, David Howells wrote: > Matt Fleming <matt@codeblueprint.co.uk> wrote: > > > On Wed, 11 Jan, at 03:27:23PM, David Howells wrote: > > > Matt Fleming <matt@codeblueprint.co.uk> wrote: > > > > > > > > + movb $0, BP_secure_boot(%rsi) > > > > > #ifdef CONFIG_EFI_STUB > > > > > /* > > > > > * The entry point for the PE/COFF executable is efi_pe_entry, so > > > > > > > > Is clearing ::secure_boot really necessary? Any code path that goes > > > > via efi_main() will set it correctly and all other code paths should > > > > get it cleared in sanitize_boot_params(), no? > > > > > > No. > > > > > > The boot_params->secure_boot parameter exists whether or not efi_main() is > > > traversed (ie. if EFI isn't enabled or CONFIG_EFI_STUB=n) and, if not cleared, > > > is of uncertain value. > > > > > > Further, sanitize_boot_params() has to be modified by this patch so as not to > > > clobber the secure_boot flag. > > > > Any new parameters that boot loaders do not know about should be > > cleared to zero by default in the boot loader because boot_params > > itself should be zero'd when allocated. > > Do you mean the boot loader or the boot wrapper? If the loader, that is > outside my control - and given the purpose of the value, I'm not sure I > want to rely on that. The boot loader, not the wrapper unless there is no boot loader, such as when the kernel image is loaded directly via EFI firmware (the original EFI stub use case). > > There are two cases to consider: > > > > 1) boot_params is not zero'd > > 2) boot_params is zero'd > > > > 1) This is a broken boot loader implementation that violates the x86 > > boot specification and I would never expect ->secure_boot to have a > > valid value. > > If there's a boot specification that must be complied with, why does > sanitize_boot_params() even exist? Why does the comment on it say: > > * Deal with bootloaders which fail to initialize unknown fields in > * boot_params to zero. The list fields in this list are taken from > * analysis of kexec-tools; if other broken bootloaders initialize a > * different set of fields we will need to figure out how to disambiguate. It exists to catch those boot loaders that don't keep to the spec, e.g. kexec-tools. > > It should not be special-cased in sanitize_boot_params(), it should be > > zero'd. > > Sigh. sanitize_boot_params() is part of the problem. The startup sequence > goes something like this: > > (0) We enter the boot wrapper. > > (1) We clear the secure-boot status value [my patch adds this]. > > (2) The boot wrapper *may* invoke efi_main() - which will determine the > secure-boot status. > > (3) The boot wrapper calls extract_kernel() to decompress the kernel. > > (4) extract_kernel() calls sanitize_boot_params() which would otherwise clear > the secure-boot flag. The ->sentinel flag should be clear (because you zero'd boot_params on alloc), so the code inside of sanitize_boot_params() should never trigger for the secure boot case. The comment for 'struct boot_params' explains this better than I can: /* * The sentinel is set to a nonzero value (0xff) in header.S. * * A bootloader is supposed to only take setup_header and put * it into a clean boot_params buffer. If it turns out that * it is clumsy or too generous with the buffer, it most * probably will pick up the sentinel variable too. The fact * that this variable then is still 0xff will let kernel * know that some variables in boot_params are invalid and * kernel should zero out certain portions of boot_params. */ __u8 sentinel; /* 0x1ef */ > (5) The boot wrapper jumps into the main kernel image, which now does not see > the secure boot status value we calculated. > > So, no, sanitize_boot_params() must *not* zero the value unless we change the > call point for s_b_p(). See the point above about the ->sentinel flag. > > 2) In this case ->secure_boot should be zero unless modified inside of > > efi_main(). > > I have no idea whether this is guaranteed or not. > > > Did you hit the scenario where ->secure_boot has a garbage value while > > developing these patches? I wouldn't expect to see it in practice. > > I haven't actually checked what the value was before I cleared it. But, I've > found that security people get seriously paranoid about assuming things to be > implicitly so;-). The thing is, we don't clear any older fields explicitly and we can't clear not-yet-invented new fields in the future if they're being set by the boot loader and not in the EFI stub. Let's not make this one field unnecessarily different from everything else. Additionally, the way you've written the code prohibits someone from adding secure boot support to a boot loader that doesn't enter the kernel via the EFI stub. I have no idea whether any boot loaders exist that work that way, but I don't see why we should actively prohibit them from working, when allowing them to work is as simple as: Don't zero the field in the boot stub. -- To unsubscribe from this list: send the line "unsubscribe linux-security-module" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Matt Fleming <matt@codeblueprint.co.uk> wrote: > > (4) extract_kernel() calls sanitize_boot_params() which would otherwise clear > > the secure-boot flag. > > The ->sentinel flag should be clear (because you zero'd boot_params on > alloc), so the code inside of sanitize_boot_params() should never > trigger for the secure boot case. But it *does* trigger, otherwise I wouldn't've noticed this. David -- To unsubscribe from this list: send the line "unsubscribe linux-security-module" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Hi all, There's an interesting issue with the way the x86 boot parameters are passed into the kernel if we want to store the secure-boot mode flag in there. My patches add boot_params->secure_boot, into which is placed the secure boot mode as deduced by the EFI boot wrapper, if it is invoked. This, however, gets scrubbed by sanitize_boot_params() if the ->sentinel flag is set. It turns out that grub2 has a bug in it whereby it initialises boot_params by copying the wrong stuff over it, thereby setting the ->sentinel flag. In my patch I saw that sanitisation was happening and I stopped sanitize_boot_params() from clobbering that particular byte and instead zeroed it on entry to the boot wrapper. This seemed reasonable since the boot wrapper calculates the flag and simply overwrites whatever the boot loader had placed there - and the value was getting clobbered by sanitisation called during kernel decompression. Matt argues, however, that boot_params->secure_boot should be propagated from the bootloader and if the bootloader wants to set it, then we should skip the check in efi_main() and go with the bootloader's opinion. This is something we probably want to do with kexec() so that the lockdown state is propagated there. However, what should happen in the core kernel if the bootloader doesn't properly initialise ->sentinel and sanitisation is done that then clobbers ->secure_boot? Should the kernel be locked down by default or left open by default if lockdown was enabled in the kernel config? But, as I mentioned, a bug in grub2 whereby it is copying the wrong initialisation data over boot_params is causing sanitisation to be triggered. Some questions that should clarify how we proceed: (1) Do we actually want to propagate the mode determination from the boot loader? (2) Do we have to determine the secure-boot status in the EFI boot wrapper (we don't use it there) or can we determine it in the core kernel? (3) What's the default mode in the case of sanitisation when lockdown is configured? (4) How do we handle the initialisation being mucked up such that ->sentinel ends up 0 and ->secure_boot ends up essentially random? Any thoughts? David -- To unsubscribe from this list: send the line "unsubscribe linux-security-module" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
On Mon, 30 Jan, at 12:10:29PM, David Howells wrote: > > Matt argues, however, that boot_params->secure_boot should be propagated from > the bootloader and if the bootloader wants to set it, then we should skip the > check in efi_main() and go with the bootloader's opinion. This is something > we probably want to do with kexec() so that the lockdown state is propagated > there. Actually what I was arguing for was that if the boot loader wants to set it and bypass the EFI boot stub, e.g. by going via the legacy 64-bit entry point, startup_64, then we should allow that as well as setting the flag in the EFI boot stub. -- To unsubscribe from this list: send the line "unsubscribe linux-security-module" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Matt Fleming <matt@codeblueprint.co.uk> wrote: > > Matt argues, however, that boot_params->secure_boot should be propagated from > > the bootloader and if the bootloader wants to set it, then we should skip the > > check in efi_main() and go with the bootloader's opinion. This is something > > we probably want to do with kexec() so that the lockdown state is propagated > > there. > > Actually what I was arguing for was that if the boot loader wants to > set it and bypass the EFI boot stub, e.g. by going via the legacy > 64-bit entry point, startup_64, then we should allow that as well as > setting the flag in the EFI boot stub. That brings up another question: Should the non-EFI entry points clear the secure_boot mode flag and set a default? David -- To unsubscribe from this list: send the line "unsubscribe linux-security-module" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
On Mon, 30 Jan, at 02:01:32PM, David Howells wrote: > Matt Fleming <matt@codeblueprint.co.uk> wrote: > > > > Matt argues, however, that boot_params->secure_boot should be propagated from > > > the bootloader and if the bootloader wants to set it, then we should skip the > > > check in efi_main() and go with the bootloader's opinion. This is something > > > we probably want to do with kexec() so that the lockdown state is propagated > > > there. > > > > Actually what I was arguing for was that if the boot loader wants to > > set it and bypass the EFI boot stub, e.g. by going via the legacy > > 64-bit entry point, startup_64, then we should allow that as well as > > setting the flag in the EFI boot stub. > > That brings up another question: Should the non-EFI entry points clear the > secure_boot mode flag and set a default? There are no non-EFI boot entry points. EFI worked before we added the EFI boot stub. The boot stub just provides new features (and allows us to bundle firmware/boot fixes workarounds with kernel updates). This is exactly why we should allow, or at least not actively prohibit, the boot loader to set ->secure_boot and jump to the old entry point if it wants to do that. -- To unsubscribe from this list: send the line "unsubscribe linux-security-module" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
diff --git a/Documentation/x86/zero-page.txt b/Documentation/x86/zero-page.txt index 95a4d34af3fd..b8527c6b7646 100644 --- a/Documentation/x86/zero-page.txt +++ b/Documentation/x86/zero-page.txt @@ -31,6 +31,8 @@ Offset Proto Name Meaning 1E9/001 ALL eddbuf_entries Number of entries in eddbuf (below) 1EA/001 ALL edd_mbr_sig_buf_entries Number of entries in edd_mbr_sig_buffer (below) +1EB/001 ALL kbd_status Numlock is enabled +1EC/001 ALL secure_boot Secure boot is enabled in the firmware 1EF/001 ALL sentinel Used to detect broken bootloaders 290/040 ALL edd_mbr_sig_buffer EDD MBR signatures 2D0/A00 ALL e820_map E820 memory map table diff --git a/arch/x86/boot/compressed/eboot.c b/arch/x86/boot/compressed/eboot.c index c8c32ebcdfdb..5b151c262ac2 100644 --- a/arch/x86/boot/compressed/eboot.c +++ b/arch/x86/boot/compressed/eboot.c @@ -1158,6 +1158,8 @@ struct boot_params *efi_main(struct efi_config *c, else setup_boot_services32(efi_early); + boot_params->secure_boot = efi_get_secureboot(sys_table); + setup_graphics(boot_params); setup_efi_pci(boot_params); diff --git a/arch/x86/boot/compressed/head_32.S b/arch/x86/boot/compressed/head_32.S index d85b9625e836..c635f7e32f5c 100644 --- a/arch/x86/boot/compressed/head_32.S +++ b/arch/x86/boot/compressed/head_32.S @@ -61,6 +61,7 @@ __HEAD ENTRY(startup_32) + movb $0, BP_secure_boot(%esi) #ifdef CONFIG_EFI_STUB jmp preferred_addr diff --git a/arch/x86/boot/compressed/head_64.S b/arch/x86/boot/compressed/head_64.S index beab8322f72a..ccd2c7461b7f 100644 --- a/arch/x86/boot/compressed/head_64.S +++ b/arch/x86/boot/compressed/head_64.S @@ -244,6 +244,7 @@ ENTRY(startup_64) * that maps our entire kernel(text+data+bss+brk), zero page * and command line. */ + movb $0, BP_secure_boot(%rsi) #ifdef CONFIG_EFI_STUB /* * The entry point for the PE/COFF executable is efi_pe_entry, so diff --git a/arch/x86/include/asm/bootparam_utils.h b/arch/x86/include/asm/bootparam_utils.h index 4a8cb8d7cbd5..7e16d53ff6a3 100644 --- a/arch/x86/include/asm/bootparam_utils.h +++ b/arch/x86/include/asm/bootparam_utils.h @@ -38,9 +38,10 @@ static void sanitize_boot_params(struct boot_params *boot_params) memset(&boot_params->ext_ramdisk_image, 0, (char *)&boot_params->efi_info - (char *)&boot_params->ext_ramdisk_image); - memset(&boot_params->kbd_status, 0, + boot_params->kbd_status = 0; + memset(&boot_params->_pad5, 0, (char *)&boot_params->hdr - - (char *)&boot_params->kbd_status); + (char *)&boot_params->_pad5); memset(&boot_params->_pad7[0], 0, (char *)&boot_params->edd_mbr_sig_buffer[0] - (char *)&boot_params->_pad7[0]); diff --git a/arch/x86/include/uapi/asm/bootparam.h b/arch/x86/include/uapi/asm/bootparam.h index b10bf319ed20..5138dacf8bb8 100644 --- a/arch/x86/include/uapi/asm/bootparam.h +++ b/arch/x86/include/uapi/asm/bootparam.h @@ -135,7 +135,8 @@ struct boot_params { __u8 eddbuf_entries; /* 0x1e9 */ __u8 edd_mbr_sig_buf_entries; /* 0x1ea */ __u8 kbd_status; /* 0x1eb */ - __u8 _pad5[3]; /* 0x1ec */ + __u8 secure_boot; /* 0x1ec */ + __u8 _pad5[2]; /* 0x1ed */ /* * The sentinel is set to a nonzero value (0xff) in header.S. * diff --git a/arch/x86/kernel/asm-offsets.c b/arch/x86/kernel/asm-offsets.c index c62e015b126c..de827d6ac8c2 100644 --- a/arch/x86/kernel/asm-offsets.c +++ b/arch/x86/kernel/asm-offsets.c @@ -81,6 +81,7 @@ void common(void) { BLANK(); OFFSET(BP_scratch, boot_params, scratch); + OFFSET(BP_secure_boot, boot_params, secure_boot); OFFSET(BP_loadflags, boot_params, hdr.loadflags); OFFSET(BP_hardware_subarch, boot_params, hdr.hardware_subarch); OFFSET(BP_version, boot_params, hdr.version); diff --git a/drivers/firmware/efi/libstub/Makefile b/drivers/firmware/efi/libstub/Makefile index 6621b13c370f..9af966863612 100644 --- a/drivers/firmware/efi/libstub/Makefile +++ b/drivers/firmware/efi/libstub/Makefile @@ -28,7 +28,7 @@ OBJECT_FILES_NON_STANDARD := y # Prevents link failures: __sanitizer_cov_trace_pc() is not linked in. KCOV_INSTRUMENT := n -lib-y := efi-stub-helper.o gop.o +lib-y := efi-stub-helper.o gop.o secureboot.o # include the stub's generic dependencies from lib/ when building for ARM/arm64 arm-deps := fdt_rw.c fdt_ro.c fdt_wip.c fdt.c fdt_empty_tree.c fdt_sw.c sort.c diff --git a/drivers/firmware/efi/libstub/arm-stub.c b/drivers/firmware/efi/libstub/arm-stub.c index b4f7d78f9e8b..9984d0442442 100644 --- a/drivers/firmware/efi/libstub/arm-stub.c +++ b/drivers/firmware/efi/libstub/arm-stub.c @@ -20,52 +20,6 @@ bool __nokaslr; -static int efi_get_secureboot(efi_system_table_t *sys_table_arg) -{ - static efi_char16_t const sb_var_name[] = { - 'S', 'e', 'c', 'u', 'r', 'e', 'B', 'o', 'o', 't', 0 }; - static efi_char16_t const sm_var_name[] = { - 'S', 'e', 't', 'u', 'p', 'M', 'o', 'd', 'e', 0 }; - - efi_guid_t var_guid = EFI_GLOBAL_VARIABLE_GUID; - efi_get_variable_t *f_getvar = sys_table_arg->runtime->get_variable; - u8 val; - unsigned long size = sizeof(val); - efi_status_t status; - - status = f_getvar((efi_char16_t *)sb_var_name, (efi_guid_t *)&var_guid, - NULL, &size, &val); - - if (status != EFI_SUCCESS) - goto out_efi_err; - - if (val == 0) - return 0; - - status = f_getvar((efi_char16_t *)sm_var_name, (efi_guid_t *)&var_guid, - NULL, &size, &val); - - if (status != EFI_SUCCESS) - goto out_efi_err; - - if (val == 1) - return 0; - - return 1; - -out_efi_err: - switch (status) { - case EFI_NOT_FOUND: - return 0; - case EFI_DEVICE_ERROR: - return -EIO; - case EFI_SECURITY_VIOLATION: - return -EACCES; - default: - return -EINVAL; - } -} - efi_status_t efi_open_volume(efi_system_table_t *sys_table_arg, void *__image, void **__fh) { @@ -226,7 +180,7 @@ unsigned long efi_entry(void *handle, efi_system_table_t *sys_table, efi_guid_t loaded_image_proto = LOADED_IMAGE_PROTOCOL_GUID; unsigned long reserve_addr = 0; unsigned long reserve_size = 0; - int secure_boot = 0; + enum efi_secureboot_mode secure_boot; struct screen_info *si; /* Check if we were booted by the EFI firmware */ @@ -296,19 +250,14 @@ unsigned long efi_entry(void *handle, efi_system_table_t *sys_table, pr_efi_err(sys_table, "Failed to parse EFI cmdline options\n"); secure_boot = efi_get_secureboot(sys_table); - if (secure_boot > 0) - pr_efi(sys_table, "UEFI Secure Boot is enabled.\n"); - - if (secure_boot < 0) { - pr_efi_err(sys_table, - "could not determine UEFI Secure Boot status.\n"); - } /* - * Unauthenticated device tree data is a security hazard, so - * ignore 'dtb=' unless UEFI Secure Boot is disabled. + * Unauthenticated device tree data is a security hazard, so ignore + * 'dtb=' unless UEFI Secure Boot is disabled. We assume that secure + * boot is enabled if we can't determine its state. */ - if (secure_boot != 0 && strstr(cmdline_ptr, "dtb=")) { + if (secure_boot != efi_secureboot_mode_disabled && + strstr(cmdline_ptr, "dtb=")) { pr_efi(sys_table, "Ignoring DTB from command line.\n"); } else { status = handle_cmdline_files(sys_table, image, cmdline_ptr, diff --git a/drivers/firmware/efi/libstub/secureboot.c b/drivers/firmware/efi/libstub/secureboot.c new file mode 100644 index 000000000000..62d6904da800 --- /dev/null +++ b/drivers/firmware/efi/libstub/secureboot.c @@ -0,0 +1,63 @@ +/* + * Secure boot handling. + * + * Copyright (C) 2013,2014 Linaro Limited + * Roy Franz <roy.franz@linaro.org + * Copyright (C) 2013 Red Hat, Inc. + * Mark Salter <msalter@redhat.com> + * + * This file is part of the Linux kernel, and is made available under the + * terms of the GNU General Public License version 2. + * + */ + +#include <linux/efi.h> +#include <asm/efi.h> + +/* BIOS variables */ +static const efi_guid_t efi_variable_guid = EFI_GLOBAL_VARIABLE_GUID; +static const efi_char16_t const efi_SecureBoot_name[] = { + 'S', 'e', 'c', 'u', 'r', 'e', 'B', 'o', 'o', 't', 0 +}; +static const efi_char16_t const efi_SetupMode_name[] = { + 'S', 'e', 't', 'u', 'p', 'M', 'o', 'd', 'e', 0 +}; + +#define get_efi_var(name, vendor, ...) \ + efi_call_runtime(get_variable, \ + (efi_char16_t *)(name), (efi_guid_t *)(vendor), \ + __VA_ARGS__); + +/* + * Determine whether we're in secure boot mode. + */ +enum efi_secureboot_mode efi_get_secureboot(efi_system_table_t *sys_table_arg) +{ + u8 secboot, setupmode; + unsigned long size; + efi_status_t status; + + size = sizeof(secboot); + status = get_efi_var(efi_SecureBoot_name, &efi_variable_guid, + NULL, &size, &secboot); + if (status != EFI_SUCCESS) + goto out_efi_err; + + size = sizeof(setupmode); + status = get_efi_var(efi_SetupMode_name, &efi_variable_guid, + NULL, &size, &setupmode); + if (status != EFI_SUCCESS) + goto out_efi_err; + + if (secboot == 0 || setupmode == 1) + return efi_secureboot_mode_disabled; + + pr_efi(sys_table_arg, "UEFI Secure Boot is enabled.\n"); + return efi_secureboot_mode_enabled; + +out_efi_err: + pr_efi_err(sys_table_arg, "Could not determine UEFI Secure Boot status.\n"); + if (status == EFI_NOT_FOUND) + return efi_secureboot_mode_disabled; + return efi_secureboot_mode_unknown; +} diff --git a/include/linux/efi.h b/include/linux/efi.h index c7904556d7a8..92e23f03045e 100644 --- a/include/linux/efi.h +++ b/include/linux/efi.h @@ -1477,6 +1477,14 @@ efi_status_t efi_setup_gop(efi_system_table_t *sys_table_arg, bool efi_runtime_disabled(void); extern void efi_call_virt_check_flags(unsigned long flags, const char *call); +enum efi_secureboot_mode { + efi_secureboot_mode_unset, + efi_secureboot_mode_unknown, + efi_secureboot_mode_disabled, + efi_secureboot_mode_enabled, +}; +enum efi_secureboot_mode efi_get_secureboot(efi_system_table_t *sys_table); + /* * Arch code can implement the following three template macros, avoiding * reptition for the void/non-void return cases of {__,}efi_call_virt():
Get the firmware's secure-boot status in the kernel boot wrapper and stash it somewhere that the main kernel image can find. The efi_get_secureboot() function is extracted from the arm stub and (a) generalised so that it can be called from x86 and (b) made to use efi_call_runtime() so that it can be run in mixed-mode. Suggested-by: Lukas Wunner <lukas@wunner.de> Signed-off-by: David Howells <dhowells@redhat.com> --- Documentation/x86/zero-page.txt | 2 + arch/x86/boot/compressed/eboot.c | 2 + arch/x86/boot/compressed/head_32.S | 1 arch/x86/boot/compressed/head_64.S | 1 arch/x86/include/asm/bootparam_utils.h | 5 +- arch/x86/include/uapi/asm/bootparam.h | 3 + arch/x86/kernel/asm-offsets.c | 1 drivers/firmware/efi/libstub/Makefile | 2 - drivers/firmware/efi/libstub/arm-stub.c | 63 +++-------------------------- drivers/firmware/efi/libstub/secureboot.c | 63 +++++++++++++++++++++++++++++ include/linux/efi.h | 8 ++++ 11 files changed, 90 insertions(+), 61 deletions(-) create mode 100644 drivers/firmware/efi/libstub/secureboot.c -- To unsubscribe from this list: send the line "unsubscribe linux-security-module" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html