From 1a74e5117717cdfae3c674e0d498b6c93f0e30c8 Mon Sep 17 00:00:00 2001
From: "Lee, Chun-Yi" <jlee@suse.com>
Date: Tue, 12 Dec 2017 20:23:17 +0800
Subject: [PATCH 10/11] PM / hibernate: a option to request that snapshot image
 must be validly signed
Patch-mainline: No, will be submitted to upstream
References: fate#316350

This kernel option is similar with the option for kernel module signature
verification. When the option is unselected, kernel will be tainted by
restoring from a unsigned snapshot image or the image was signed by a
wrong key.

When the option is selected, kernel will refuse the system to be resumed
if the signature verification did not pass. The hibernation resume
process will be stopped and whole system just boots as normal.

Signed-off-by: Lee, Chun-Yi <jlee@suse.com>
---
 Documentation/admin-guide/kernel-parameters.txt |    4 ++
 include/linux/kernel.h                          |    3 +
 kernel/panic.c                                  |    2 +
 kernel/power/Kconfig                            |    9 +++++
 kernel/power/hibernate.c                        |    2 +
 kernel/power/power.h                            |    4 ++
 kernel/power/snapshot.c                         |   39 ++++++++++++++++++++++--
 7 files changed, 59 insertions(+), 4 deletions(-)

--- a/Documentation/admin-guide/kernel-parameters.txt
+++ b/Documentation/admin-guide/kernel-parameters.txt
@@ -3612,6 +3612,10 @@
 		protect_image	Turn on image protection during restoration
 				(that will set all pages holding image data
 				during restoration read-only).
+		sigenforce	Refuse the system to be resumed from a snapshot
+				image which is unsigned or signed by a wrong key.
+				This option equals to
+				CONFIG_HIBERNATE_VERIFICATION_FORCE=y
 
 	retain_initrd	[RAM] Keep initrd memory after extraction
 
--- a/include/linux/kernel.h
+++ b/include/linux/kernel.h
@@ -527,6 +527,7 @@ extern enum system_states {
 #define TAINT_UNSIGNED_MODULE		13
 #define TAINT_SOFTLOCKUP		14
 #define TAINT_LIVEPATCH			15
+#define TAINT_UNSAFE_HIBERNATE		16
 /* !!! Keep TAINT_FLAGS_COUNT in sync !!! */
 
 #ifdef CONFIG_SUSE_KERNEL_SUPPORTED
@@ -538,7 +539,7 @@ extern enum system_states {
 #define TAINT_EXTERNAL_SUPPORT		31
 #define TAINT_FLAGS_COUNT		32
 #else
-#define TAINT_FLAGS_COUNT		16
+#define TAINT_FLAGS_COUNT		17
 #endif
 
 struct taint_flag {
--- a/kernel/panic.c
+++ b/kernel/panic.c
@@ -321,6 +321,7 @@ const struct taint_flag taint_flags[TAIN
 	{ 'E', ' ', true },	/* TAINT_UNSIGNED_MODULE */
 	{ 'L', ' ', false },	/* TAINT_SOFTLOCKUP */
 	{ 'K', ' ', true },	/* TAINT_LIVEPATCH */
+	{ 'H', ' ', false },	/* TAINT_UNSAFE_HIBERNATE */
 #ifdef CONFIG_SUSE_KERNEL_SUPPORTED
 	{ 'N', ' ', true },	/* TAINT_NO_SUPPORT */
 	{ 'X', ' ', true },	/* TAINT_EXTERNAL_SUPPORT */
@@ -346,6 +347,7 @@ const struct taint_flag taint_flags[TAIN
  *  'E' - Unsigned module has been loaded.
  *  'L' - A soft lockup has previously occurred.
  *  'K' - Kernel has been live patched.
+ *  'H' - System restored from unsafe hibernate snapshot image.
  *  'N' - Unsuported modules loaded.
  *  'X' - Modules with external support loaded.
  *
--- a/kernel/power/Kconfig
+++ b/kernel/power/Kconfig
@@ -89,6 +89,15 @@ config HIBERNATE_VERIFICATION
 	  key for hibernate verification. This function will be bypassed on
 	  legacy BIOS.
 
+config HIBERNATE_VERIFICATION_FORCE
+	bool "Require hibernate snapshot image to be validly signed"
+	depends on HIBERNATE_VERIFICATION
+	help
+	  Refuse the system to be resumed from a snapshot image which is
+	  unsigned or signed by a wrong key. If this option is not enabled,
+	  the system can be resumed by unsigned snapshot image and kernel
+	  will be tainted.
+
 config ARCH_SAVE_PAGE_KEYS
 	bool
 
--- a/kernel/power/hibernate.c
+++ b/kernel/power/hibernate.c
@@ -1182,6 +1182,8 @@ static int __init hibernate_setup(char *
 	} else if (IS_ENABLED(CONFIG_STRICT_KERNEL_RWX)
 		   && !strncmp(str, "protect_image", 13)) {
 		enable_restore_image_protection();
+	} else if (!strncmp(str, "sigenforce", 10)) {
+		snapshot_set_enforce_verify();
 	}
 	return 1;
 }
--- a/kernel/power/power.h
+++ b/kernel/power/power.h
@@ -171,10 +171,14 @@ extern void snapshot_free_trampoline(voi
 extern int snapshot_image_verify(void);
 extern int swsusp_prepare_hash(bool may_sleep);
 extern void swsusp_finish_hash(void);
+extern void snapshot_set_enforce_verify(void);
+extern int snapshot_is_enforce_verify(void);
 #else
 static inline int snapshot_image_verify(void) { return 0; }
 static inline int swsusp_prepare_hash(bool may_sleep) { return 0; }
 static inline void swsusp_finish_hash(void) {}
+static inline void snapshot_set_enforce_verify(void) {}
+static inline int snapshot_is_enforce_verify(void) {return 0;}
 #endif
 
 /* If unset, the snapshot device cannot be open. */
--- a/kernel/power/snapshot.c
+++ b/kernel/power/snapshot.c
@@ -90,6 +90,7 @@ static inline void hibernate_restore_unp
  * to image kernel.
  */
 struct trampoline {
+	int sig_enforce;
 	int sig_verify_ret;
 	bool secret_key_valid;
 	u8 secret_key[SECRET_KEY_SIZE];
@@ -1438,6 +1439,23 @@ static u8 signature[SNAPSHOT_DIGEST_SIZE
 /* Keep the signature verification result for trampoline */
 static int sig_verify_ret;
 
+/* enforce the snapshot must be signed */
+#ifdef CONFIG_HIBERNATE_VERIFICATION_FORCE
+static bool sig_enforce = true;
+#else
+static bool sig_enforce;
+#endif
+
+void snapshot_set_enforce_verify(void)
+{
+	sig_enforce = true;
+}
+
+int snapshot_is_enforce_verify(void)
+{
+	return sig_enforce;
+}
+
 static u8 *s4_verify_digest;
 static struct shash_desc *s4_verify_desc;
 
@@ -1451,7 +1469,7 @@ int swsusp_prepare_hash(bool may_sleep)
 	key = get_efi_secret_key();
 	if (!key) {
 		pr_warn_once("PM: secret key is invalid\n");
-		return -EINVAL;
+		return (sig_enforce) ? -EINVAL : 0;
 	}
 
 	tfm = crypto_alloc_shash(SNAPSHOT_HMAC, 0, 0);
@@ -1546,6 +1564,8 @@ int snapshot_image_verify(void)
 		pr_warn("PM: Signature verification failed: %d\n", ret);
  error:
 	sig_verify_ret = ret;
+	if (!sig_enforce)
+		ret = 0;
 	return ret;
 }
 
@@ -1619,17 +1639,30 @@ static void load_signature(struct swsusp
 
 static void init_sig_verify(struct trampoline *t)
 {
+	t->sig_enforce = sig_enforce;
 	t->sig_verify_ret = sig_verify_ret;
 	sig_verify_ret = 0;
 }
 
 static void handle_sig_verify(struct trampoline *t)
 {
-	if (t->sig_verify_ret)
+	sig_enforce = t->sig_enforce;
+	if (sig_enforce)
+		pr_info("PM: Enforce the snapshot to be validly signed\n");
+
+	if (t->sig_verify_ret) {
 		pr_warn("PM: Signature verification failed: %d\n",
 			t->sig_verify_ret);
-	else if (t->secret_key_valid)
+		/* taint kernel */
+		if (!sig_enforce) {
+			pr_warn("PM: System resumed from unsafe snapshot - "
+				"tainting kernel\n");
+			add_taint(TAINT_UNSAFE_HIBERNATE, LOCKDEP_STILL_OK);
+			pr_info("%s\n", print_tainted());
+		}
+	} else if (t->secret_key_valid) {
 		pr_info("PM: Signature verification passed.\n");
+	}
 }
 #else
 static int
