From: Ben Hutchings <ben@decadent.org.uk>
Date: Tue, 10 Sep 2019 11:54:28 +0100
Subject: efi: Lock down the kernel if booted in secure boot mode

Based on an earlier patch by David Howells, who wrote the following
description:

> UEFI Secure Boot provides a mechanism for ensuring that the firmware will
> only load signed bootloaders and kernels.  Certain use cases may also
> require that all kernel modules also be signed.  Add a configuration option
> that to lock down the kernel - which includes requiring validly signed
> modules - if the kernel is secure-booted.

Signed-off-by: Ben Hutchings <ben@decadent.org.uk>
[Salvatore Bonaccorso: After fixing https://bugs.debian.org/956197 the
help text for LOCK_DOWN_IN_EFI_SECURE_BOOT was adjusted to mention that
lockdown is triggered in integrity mode (https://bugs.debian.org/1025417)]
Signed-off-by: Salvatore Bonaccorso <carnil@debian.org>
---
 arch/x86/kernel/setup.c           |    4 ++--
 drivers/firmware/efi/secureboot.c |    3 +++
 include/linux/security.h          |    6 ++++++
 security/lockdown/Kconfig         |   15 +++++++++++++++
 security/lockdown/lockdown.c      |    2 +-
 5 files changed, 27 insertions(+), 3 deletions(-)

--- a/arch/x86/kernel/setup.c
+++ b/arch/x86/kernel/setup.c
@@ -900,6 +900,8 @@ void __init setup_arch(char **cmdline_p)
 	if (efi_enabled(EFI_BOOT))
 		efi_init();
 
+	efi_set_secure_boot(boot_params.secure_boot);
+
 	reserve_ibft_region();
 	x86_init.resources.dmi_setup();
 
@@ -1061,8 +1063,6 @@ void __init setup_arch(char **cmdline_p)
 	/* Allocate bigger log buffer */
 	setup_log_buf(1);
 
-	efi_set_secure_boot(boot_params.secure_boot);
-
 	reserve_initrd();
 
 	acpi_table_upgrade();
--- a/drivers/firmware/efi/secureboot.c
+++ b/drivers/firmware/efi/secureboot.c
@@ -15,6 +15,7 @@
 #include <linux/efi.h>
 #include <linux/kernel.h>
 #include <linux/printk.h>
+#include <linux/security.h>
 
 /*
  * Decide what to do when UEFI secure boot mode is enabled.
@@ -28,6 +29,10 @@ void __init efi_set_secure_boot(enum efi
 			break;
 		case efi_secureboot_mode_enabled:
 			set_bit(EFI_SECURE_BOOT, &efi.flags);
+#ifdef CONFIG_LOCK_DOWN_IN_EFI_SECURE_BOOT
+			lock_kernel_down("EFI Secure Boot",
+					 LOCKDOWN_INTEGRITY_MAX);
+#endif
 			pr_info("Secure boot enabled\n");
 			break;
 		default:
--- a/include/linux/security.h
+++ b/include/linux/security.h
@@ -509,6 +509,7 @@ int security_inode_getsecctx(struct inode *inode, void **ctx, u32 *ctxlen);
 int security_locked_down(enum lockdown_reason what);
 int lsm_fill_user_ctx(struct lsm_ctx __user *uctx, u32 *uctx_len,
 		      void *val, size_t val_len, u64 id, u64 flags);
+int lock_kernel_down(const char *where, enum lockdown_reason level);
 #else /* CONFIG_SECURITY */
 
 static inline int call_blocking_lsm_notifier(enum lsm_event event, void *data)
@@ -1483,6 +1484,11 @@ static inline int lsm_fill_user_ctx(struct lsm_ctx __user *uctx,
 {
 	return -EOPNOTSUPP;
 }
+static inline int
+lock_kernel_down(const char *where, enum lockdown_reason level)
+{
+	return -EOPNOTSUPP;
+}
 #endif	/* CONFIG_SECURITY */
 
 #if defined(CONFIG_SECURITY) && defined(CONFIG_WATCH_QUEUE)
--- a/security/lockdown/Kconfig
+++ b/security/lockdown/Kconfig
@@ -45,3 +45,18 @@ config LOCK_DOWN_KERNEL_FORCE_CONFIDENTI
 	 disabled.
 
 endchoice
+
+config LOCK_DOWN_IN_EFI_SECURE_BOOT
+	bool "Lock down the kernel in EFI Secure Boot mode"
+	default n
+	depends on SECURITY_LOCKDOWN_LSM
+	depends on EFI
+	select SECURITY_LOCKDOWN_LSM_EARLY
+	help
+	  UEFI Secure Boot provides a mechanism for ensuring that the firmware
+	  will only load signed bootloaders and kernels.  Secure boot mode may
+	  be determined from EFI variables provided by the system firmware if
+	  not indicated by the boot parameters.
+
+	  Enabling this option results in kernel lockdown being
+	  triggered in integrity mode if EFI Secure Boot is set.
--- a/security/lockdown/lockdown.c
+++ b/security/lockdown/lockdown.c
@@ -24,7 +24,7 @@ static const enum lockdown_reason lockdo
 /*
  * Put the kernel into lock-down mode.
  */
-static int lock_kernel_down(const char *where, enum lockdown_reason level)
+int lock_kernel_down(const char *where, enum lockdown_reason level)
 {
 	if (kernel_locked_down >= level)
 		return -EPERM;
