2015-08-11 06:14:14

by Lee, Chun-Yi

[permalink] [raw]
Subject: [PATCH v2 00/16] Signature verification of hibernate snapshot

Hi experts,

This patchset is the implementation of signature verification of hibernate
snapshot image. The origin idea is from Jiri Kosina: Let EFI bootloader
generate key-pair in UEFI secure boot environment, then forward it to kernel
for sign/verify hibernate image.

The first patchset for this function was sent in Sep. 2013, the implementation
is base on PKI. This new patchset is base on HMAC-SHA1.

The hibernate function provided by kernel was used to snapshot memory
to be a image for keeping in storage, then restored in appropriate time.
There have potential threat from hacking the memory snapshot image.
Cracker may triggers hibernating process through ioctl to grab snapshot
image, then restoring modified image back to memory. Another situation
is booting to other hacked OS to modify the snapshot image in swap
partition or file, then user may runs malware after image restored to
memory. In addition, the above weakness cause kernel is not fully trusted
in EFI secure boot environment.

So, kernel hibernate function needs a mechanism to verify integrity of
hibernate snapshot image.

For signing hibernate image, kernel need a key for generating signature of
image. The origin idea is using PKI, the EFI bootloader, shim generates key
pair and forward to boot kernel for signing/verifying image. In Linux Plumbers
Conference 2013, we got response from community experts for just using
symmetric key algorithm to generate signature, that's simpler and no EFI
bootloader's involving.

Current solution is using HMAC-SHA1 algorithm, it generating HMAC key in EFI
stub, the HMAC key stored in efi boot service variable, When hibernate
recovering, kernel will verify the image signature before switch whole system
to image kernel and image memory space. When verifying failed, kernel is
tainted or stop recovering and discarding image.

Set HIBERNATE_VERIFICATION compile option to true for enabling hibernate
verification. The default behavior of verifying failed is accept restoring
image but tainting kernel with H taint flag. Using HIBERNATE_VERIFICATION_FORCE
kernel compile option or "sigenforce" kernel parameter to force hibernate
recovery process stop when verification failed. It allows user to trigger the
key re-generating process in EFI stub through SNAPSHOT_REGENERATE_KEY ioctl.

v2:
- Replaced all SWSUSP naming with HIBERNATION.
- Moved swsusp_info structure definition only for CONFIG_HIBERNATION.
- Fixed typo in patch subject and description.
- Changed name of i8254() to read_i8254()
- Removed all efi_printk log in efi_random.c
- Changed the size of key array to be unsigned in efi_random.c
- Avoid calling cpuid many times.
- Add line breaks to error log in efi_random.c
- Removed free_handle label in efi_random.c
- Moved efi_status_to_str() to efi_random.c, and reduce code duplication.
- Set rng_handle = NULL in efi_locate_rng()
- Changed u32 random to bool in efi_rng_supported()
- Using EFI status codes explicitly.
- Modified Copyright declaration.
- Moved set_hibernation_key_regen_flag to user.c


Lee, Chun-Yi (16):
PM / hibernate: define HMAC algorithm and digest size of hibernation
x86/efi: Add get and set variable to EFI services pointer table
x86/boot: Public getting random boot function
x86/efi: Generating random number in EFI stub
x86/efi: Get entropy through EFI random number generator protocol
x86/efi: Generating random HMAC key for siging hibernate image
efi: Make efi_status_to_err() public
x86/efi: Carrying hibernation key by setup data
PM / hibernate: Reserve hibernation key and erase footprints
PM / hibernate: Generate and verify signature of hibernate snapshot
PM / hibernate: Avoid including hibernation key to hibernate image
PM / hibernate: Forward signature verifying result and key to image
kernel
PM / hibernate: Add configuration to enforce signature verification
PM / hibernate: Allow user trigger hibernation key re-generating
PM / hibernate: Bypass verification logic on legacy BIOS
PM / hibernate: Document signature verification of hibernate snapshot

Documentation/kernel-parameters.txt | 5 +
Documentation/power/swsusp-signature-verify.txt | 86 +++++++
arch/x86/boot/compressed/Makefile | 1 +
arch/x86/boot/compressed/aslr.c | 57 +----
arch/x86/boot/compressed/eboot.c | 97 ++++++++
arch/x86/boot/compressed/efi_random.c | 289 +++++++++++++++++++++++
arch/x86/boot/compressed/head_32.S | 6 +-
arch/x86/boot/compressed/head_64.S | 8 +-
arch/x86/boot/compressed/misc.c | 55 +++++
arch/x86/boot/compressed/misc.h | 4 +
arch/x86/include/asm/efi.h | 2 +
arch/x86/include/asm/suspend.h | 13 ++
arch/x86/include/uapi/asm/bootparam.h | 1 +
arch/x86/kernel/setup.c | 21 +-
arch/x86/power/Makefile | 1 +
arch/x86/power/hibernate_keys.c | 173 ++++++++++++++
drivers/firmware/Makefile | 1 +
drivers/firmware/efi/Kconfig | 4 +
drivers/firmware/efi/Makefile | 1 +
drivers/firmware/efi/efi-hibernate_keys.c | 42 ++++
drivers/firmware/efi/vars.c | 33 ---
include/linux/efi.h | 46 ++++
include/linux/kernel.h | 1 +
include/linux/suspend.h | 27 +++
include/uapi/linux/suspend_ioctls.h | 3 +-
kernel/panic.c | 2 +
kernel/power/Kconfig | 23 ++
kernel/power/hibernate.c | 10 +
kernel/power/power.h | 22 +-
kernel/power/snapshot.c | 293 ++++++++++++++++++++++--
kernel/power/swap.c | 4 +
kernel/power/user.c | 19 ++
kernel/reboot.c | 3 +
33 files changed, 1240 insertions(+), 113 deletions(-)
create mode 100644 Documentation/power/swsusp-signature-verify.txt
create mode 100644 arch/x86/boot/compressed/efi_random.c
create mode 100644 arch/x86/power/hibernate_keys.c
create mode 100644 drivers/firmware/efi/efi-hibernate_keys.c

--
2.1.4


2015-08-11 06:14:23

by Lee, Chun-Yi

[permalink] [raw]
Subject: [PATCH v2 01/16] PM / hibernate: define HMAC algorithm and digest size of hibernation

Using HMAC-SHA1 to be the HMAC algorithm of signing hibernate
snapshot image. The digest size of HMAC-SHA1 is 160 bits (20 bytes),
this size will be also applied to the length of HMAC key.

In addition, moved swsusp_info struct definition into CONFIG_HIBERNATION
ifdef block because only hibernate code uses it.
Add HIBERNATE_VERIFICATION kernel config for using by later hibernate
signature verification code.

Signed-off-by: Lee, Chun-Yi <[email protected]>
---
include/linux/suspend.h | 5 +++++
kernel/power/Kconfig | 13 +++++++++++++
kernel/power/power.h | 3 ++-
3 files changed, 20 insertions(+), 1 deletion(-)

diff --git a/include/linux/suspend.h b/include/linux/suspend.h
index 5efe743..56c6de9 100644
--- a/include/linux/suspend.h
+++ b/include/linux/suspend.h
@@ -327,6 +327,11 @@ struct platform_hibernation_ops {
};

#ifdef CONFIG_HIBERNATION
+
+/* HMAC Algorithm of Hibernate Signature */
+#define HIBERNATION_HMAC "hmac(sha1)"
+#define HIBERNATION_DIGEST_SIZE 20
+
/* kernel/power/snapshot.c */
extern void __register_nosave_region(unsigned long b, unsigned long e, int km);
static inline void __init register_nosave_region(unsigned long b, unsigned long e)
diff --git a/kernel/power/Kconfig b/kernel/power/Kconfig
index 9e30231..8608b3b 100644
--- a/kernel/power/Kconfig
+++ b/kernel/power/Kconfig
@@ -66,6 +66,19 @@ config HIBERNATION

For more information take a look at <file:Documentation/power/swsusp.txt>.

+config HIBERNATE_VERIFICATION
+ bool "Hibernate verification"
+ depends on HIBERNATION
+ depends on EFI_STUB
+ depends on X86
+ select CRYPTO_HMAC
+ select CRYPTO_SHA1
+ help
+ This option provides support for generating and verifying the
+ signature of memory snapshot image by HMAC-SHA1. Current mechanism
+ relies on UEFI secure boot environment, EFI stub generates HMAC
+ key for hibernate verification.
+
config ARCH_SAVE_PAGE_KEYS
bool

diff --git a/kernel/power/power.h b/kernel/power/power.h
index caadb56..6ea5c78 100644
--- a/kernel/power/power.h
+++ b/kernel/power/power.h
@@ -4,6 +4,7 @@
#include <linux/freezer.h>
#include <linux/compiler.h>

+#ifdef CONFIG_HIBERNATION
struct swsusp_info {
struct new_utsname uts;
u32 version_code;
@@ -12,9 +13,9 @@ struct swsusp_info {
unsigned long image_pages;
unsigned long pages;
unsigned long size;
+ u8 signature[HIBERNATION_DIGEST_SIZE];
} __aligned(PAGE_SIZE);

-#ifdef CONFIG_HIBERNATION
/* kernel/power/snapshot.c */
extern void __init hibernate_reserved_size_init(void);
extern void __init hibernate_image_size_init(void);
--
2.1.4

2015-08-11 06:15:00

by Lee, Chun-Yi

[permalink] [raw]
Subject: [PATCH v2 02/16] x86/efi: Add get and set variable to EFI services pointer table

Add get variable and set variable function to EFI services pointer
table for supporting later functions of hibernate signature
verification to keep the HMAC key in efi boot service variable.
EFI boot stub needs get/set_variable functions for accessing key.

Reviewed-by: Jiri Kosina <[email protected]>
Tested-by: Jiri Kosina <[email protected]>
Signed-off-by: Lee, Chun-Yi <[email protected]>
---
arch/x86/boot/compressed/eboot.c | 4 ++++
arch/x86/boot/compressed/head_32.S | 6 +++---
arch/x86/boot/compressed/head_64.S | 8 ++++----
arch/x86/include/asm/efi.h | 2 ++
4 files changed, 13 insertions(+), 7 deletions(-)

diff --git a/arch/x86/boot/compressed/eboot.c b/arch/x86/boot/compressed/eboot.c
index 2c82bd1..0ffb6db 100644
--- a/arch/x86/boot/compressed/eboot.c
+++ b/arch/x86/boot/compressed/eboot.c
@@ -30,12 +30,14 @@ static void setup_boot_services##bits(struct efi_config *c) \
{ \
efi_system_table_##bits##_t *table; \
efi_boot_services_##bits##_t *bt; \
+ efi_runtime_services_##bits##_t *rt; \
\
table = (typeof(table))sys_table; \
\
c->text_output = table->con_out; \
\
bt = (typeof(bt))(unsigned long)(table->boottime); \
+ rt = (typeof(rt))(unsigned long)(table->runtime); \
\
c->allocate_pool = bt->allocate_pool; \
c->allocate_pages = bt->allocate_pages; \
@@ -45,6 +47,8 @@ static void setup_boot_services##bits(struct efi_config *c) \
c->locate_handle = bt->locate_handle; \
c->handle_protocol = bt->handle_protocol; \
c->exit_boot_services = bt->exit_boot_services; \
+ c->get_variable = rt->get_variable; \
+ c->set_variable = rt->set_variable; \
}
BOOT_SERVICES(32);
BOOT_SERVICES(64);
diff --git a/arch/x86/boot/compressed/head_32.S b/arch/x86/boot/compressed/head_32.S
index 8ef964d..a7db5e3 100644
--- a/arch/x86/boot/compressed/head_32.S
+++ b/arch/x86/boot/compressed/head_32.S
@@ -54,7 +54,7 @@ ENTRY(efi_pe_entry)

/* Relocate efi_config->call() */
leal efi32_config(%esi), %eax
- add %esi, 88(%eax)
+ add %esi, 104(%eax)
pushl %eax

call make_boot_params
@@ -80,7 +80,7 @@ ENTRY(efi32_stub_entry)

/* Relocate efi_config->call() */
leal efi32_config(%esi), %eax
- add %esi, 88(%eax)
+ add %esi, 104(%eax)
pushl %eax
2:
call efi_main
@@ -230,7 +230,7 @@ relocated:
#ifdef CONFIG_EFI_STUB
.data
efi32_config:
- .fill 11,8,0
+ .fill 13,8,0
.long efi_call_phys
.long 0
.byte 0
diff --git a/arch/x86/boot/compressed/head_64.S b/arch/x86/boot/compressed/head_64.S
index b0c0d16..471b1c1 100644
--- a/arch/x86/boot/compressed/head_64.S
+++ b/arch/x86/boot/compressed/head_64.S
@@ -255,7 +255,7 @@ ENTRY(efi_pe_entry)
/*
* Relocate efi_config->call().
*/
- addq %rbp, efi64_config+88(%rip)
+ addq %rbp, efi64_config+104(%rip)

movq %rax, %rdi
call make_boot_params
@@ -275,7 +275,7 @@ handover_entry:
* Relocate efi_config->call().
*/
movq efi_config(%rip), %rax
- addq %rbp, 88(%rax)
+ addq %rbp, 104(%rax)
2:
movq efi_config(%rip), %rdi
call efi_main
@@ -448,14 +448,14 @@ efi_config:
#ifdef CONFIG_EFI_MIXED
.global efi32_config
efi32_config:
- .fill 11,8,0
+ .fill 13,8,0
.quad efi64_thunk
.byte 0
#endif

.global efi64_config
efi64_config:
- .fill 11,8,0
+ .fill 13,8,0
.quad efi_call
.byte 1
#endif /* CONFIG_EFI_STUB */
diff --git a/arch/x86/include/asm/efi.h b/arch/x86/include/asm/efi.h
index 155162e..a274aa8 100644
--- a/arch/x86/include/asm/efi.h
+++ b/arch/x86/include/asm/efi.h
@@ -175,6 +175,8 @@ struct efi_config {
u64 handle_protocol;
u64 exit_boot_services;
u64 text_output;
+ u64 get_variable;
+ u64 set_variable;
efi_status_t (*call)(unsigned long, ...);
bool is64;
} __packed;
--
2.1.4