@@ -2730,6 +2730,13 @@ bytes respectively. Such letter suffixes can also be entirely omitted.
Useful for devices that are detected asynchronously
(e.g. USB and MMC devices).
+ snapshot_sig_enforce
+ [HIBERNATE] When CONFIG_SNAPSHOT_VERIFICATION is set,
+ this means the snapshot image without (valid) signatures
+ will fail to recover. This parameter provides user to
+ force launch the snapshot signature check even the UEFI
+ secure boot didn't enable.
+
hibernate= [HIBERNATION]
noresume Don't check if there's a hibernation image
present during boot.
@@ -1591,6 +1591,17 @@ config EFI_SECURE_BOOT_SIG_ENFORCE
Say Y here to automatically enable module signature enforcement
when a system boots with UEFI Secure Boot enabled.
+config EFI_SECURE_BOOT_SNAPSHOT_SIG_ENFORCE
+ def_bool n
+ prompt "Force snapshot signing when UEFI Secure Boot is enabled"
+ ---help---
+ 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 the snapshot image of hibernate
+ also be signed.
+ Say Y here to automatically enable snapshot iage signature
+ enforcement when a system boots with UEFI Secure Boot enabled.
+
config SECCOMP
def_bool y
prompt "Enable seccomp to safely compute untrusted bytecode"
@@ -427,6 +427,7 @@ extern enum system_states {
#define TAINT_CRAP 10
#define TAINT_FIRMWARE_WORKAROUND 11
#define TAINT_OOT_MODULE 12
+#define TAINT_UNSAFE_HIBERNATE 13
extern const char hex_asc[];
#define hex_asc_lo(x) hex_asc[((x) & 0x0f)]
@@ -320,6 +320,13 @@ extern unsigned long get_safe_page(gfp_t gfp_mask);
extern void hibernation_set_ops(const struct platform_hibernation_ops *ops);
extern int hibernate(void);
extern bool system_entering_hibernation(void);
+
+#ifdef CONFIG_SNAPSHOT_VERIFICATION
+extern void enforce_signed_snapshot(void);
+#else
+static inline void enforce_signed_snapshot(void) {};
+#endif
+
#else /* CONFIG_HIBERNATION */
static inline void register_nosave_region(unsigned long b, unsigned long e) {}
static inline void register_nosave_region_late(unsigned long b, unsigned long e) {}
@@ -206,6 +206,7 @@ static const struct tnt tnts[] = {
{ TAINT_CRAP, 'C', ' ' },
{ TAINT_FIRMWARE_WORKAROUND, 'I', ' ' },
{ TAINT_OOT_MODULE, 'O', ' ' },
+ { TAINT_UNSAFE_HIBERNATE, 'H', ' ' },
};
/**
@@ -224,6 +225,7 @@ static const struct tnt tnts[] = {
* 'C' - modules from drivers/staging are loaded.
* 'I' - Working around severe firmware bug.
* 'O' - Out-of-tree module has been loaded.
+ * 'H' - System restored from unsafe hibernate snapshot image.
*
* The string is overwritten by the next call to print_tainted().
*/
@@ -17,6 +17,7 @@ struct forward_info {
static void *skey_data;
static void *forward_info_buf;
static unsigned long skey_dsize;
+static bool sig_enforce = false;
bool swsusp_page_is_sign_key(struct page *page)
{
@@ -52,6 +53,7 @@ void fill_sig_forward_info(void *page, int sig_check_ret_in)
memset(page, 0, PAGE_SIZE);
info = (struct forward_info *)page;
+ info->head.sig_enforce = sig_enforce;
info->head.sig_check_ret = sig_check_ret_in;
if (skey_data && !IS_ERR(skey_data) &&
skey_dsize <= SKEY_DBUF_MAX_SIZE) {
@@ -74,6 +76,11 @@ void restore_sig_forward_info(void)
}
info = (struct forward_info *)forward_info_buf;
+ /* eanble sig_enforce either boot kernel or resume target kernel set it */
+ sig_enforce = sig_enforce || info->head.sig_enforce;
+ if (sig_enforce)
+ pr_info("PM: Enforce S4 snapshot signature check\n");
+
sig_check_ret = info->head.sig_check_ret;
if (sig_check_ret)
pr_info("PM: Signature check fail: %d\n", sig_check_ret);
@@ -89,6 +96,14 @@ void restore_sig_forward_info(void)
/* reset skey page buffer */
memset(forward_info_buf, 0, PAGE_SIZE);
+
+ /* taint kernel */
+ if (!sig_enforce && sig_check_ret) {
+ pr_warning("PM: Hibernate signature check fail, system "
+ "restored from unsafe snapshot: tainting kernel\n");
+ add_taint(TAINT_UNSAFE_HIBERNATE, LOCKDEP_STILL_OK);
+ pr_info("%s\n", print_tainted());
+ }
}
bool skey_data_available(void)
@@ -275,6 +290,17 @@ size_t get_key_length(const struct key *key)
return len;
}
+void enforce_signed_snapshot(void)
+{
+ sig_enforce = true;
+ pr_info("PM: Enforce signature verification of hibernate snapshot\n");
+}
+
+bool sig_enforced(void)
+{
+ return sig_enforce;
+}
+
static int __init init_sign_key_data(void)
{
skey_data = (void *)get_zeroed_page(GFP_KERNEL);
@@ -290,3 +316,12 @@ static int __init init_sign_key_data(void)
}
late_initcall(init_sign_key_data);
+
+static int __init sig_enforce_setup(char *str)
+{
+ sig_enforce = true;
+ pr_info("PM: Enforce signature verification of hibernate snapshot\n");
+ return 1;
+}
+
+__setup("snapshot_sig_enforce", sig_enforce_setup);
@@ -187,6 +187,7 @@ extern void restore_sig_forward_info(void);
extern bool swsusp_page_is_sign_key(struct page *page);
extern unsigned long get_sig_forward_info_pfn(void);
extern void fill_sig_forward_info(void *page_addr, int sig_check_ret);
+extern bool sig_enforced(void);
#else
static inline bool skey_data_available(void)
{
@@ -2581,7 +2581,11 @@ int snapshot_image_verify(void)
pr_info("PM: snapshot signature check SUCCESS!\n");
forward_ret:
- snapshot_fill_sig_forward_info(ret);
+ /* forward check result when pass or not enforce verify success */
+ if (!ret || !sig_enforced()) {
+ snapshot_fill_sig_forward_info(ret);
+ ret = 0;
+ }
error_shash:
kfree(handle_buffers);
kfree(digest);
We will not direct fail the hibernate snapshot restore when the signature check fail, instead kernel will complain by warning message and taint kernel. This patch also introduced a sig_enforce flag to indicate if we want direct fail the snapshot restore when signature check fail. User can enable it through snapshot_sig_enforce parameter or EFI_SECURE_BOOT_SNAPSHOT_SIG_ENFORCE. Signed-off-by: Lee, Chun-Yi <jlee@suse.com> --- Documentation/kernel-parameters.txt | 7 +++++++ arch/x86/Kconfig | 11 +++++++++++ include/linux/kernel.h | 1 + include/linux/suspend.h | 7 +++++++ kernel/panic.c | 2 ++ kernel/power/hibernate_keys.c | 35 +++++++++++++++++++++++++++++++++++ kernel/power/power.h | 1 + kernel/power/snapshot.c | 6 +++++- 8 files changed, 69 insertions(+), 1 deletions(-)