@@ -50,6 +50,7 @@
#include <linux/init_ohci1394_dma.h>
#include <linux/kvm_para.h>
#include <linux/dma-contiguous.h>
+#include <linux/suspend.h>
#include <linux/errno.h>
#include <linux/kernel.h>
@@ -1135,6 +1136,12 @@ void __init setup_arch(char **cmdline_p)
}
#endif
+#ifdef CONFIG_EFI_SECURE_BOOT_SNAPSHOT_SIG_ENFORCE
+ if (boot_params.secure_boot) {
+ enforce_signed_snapshot();
+ }
+#endif
+
/*
* Parse the ACPI tables for possible boot-time SMP configuration.
*/
@@ -29,6 +29,7 @@
#include <linux/ctype.h>
#include <linux/genhd.h>
#include <linux/key.h>
+#include <linux/efi.h>
#include "power.h"
@@ -633,6 +634,9 @@ int hibernate(void)
{
int error;
+ if (secure_hibernate(SIG_ENFORCE | SIG_CHECK_SKEY))
+ return -EPERM;
+
lock_system_sleep();
/* The snapshot device should not be opened while we're running */
if (!atomic_add_unless(&snapshot_device_available, -1, 0)) {
@@ -800,6 +804,11 @@ static int software_resume(void)
if (error)
goto Unlock;
+ if (secure_hibernate(SIG_ENFORCE | SIG_CHECK_WKEY)) {
+ mutex_unlock(&pm_mutex);
+ return -EPERM;
+ }
+
/* The snapshot device should not be opened while we're running */
if (!atomic_add_unless(&snapshot_device_available, -1, 0)) {
error = -EBUSY;
@@ -893,6 +902,11 @@ static ssize_t disk_show(struct kobject *kobj, struct kobj_attribute *attr,
int i;
char *start = buf;
+ if (secure_hibernate(SIG_ENFORCE | SIG_CHECK_SKEY)) {
+ buf += sprintf(buf, "[%s]\n", "disabled");
+ return buf-start;
+ }
+
for (i = HIBERNATION_FIRST; i <= HIBERNATION_MAX; i++) {
if (!hibernation_modes[i])
continue;
@@ -927,6 +941,9 @@ static ssize_t disk_store(struct kobject *kobj, struct kobj_attribute *attr,
char *p;
int mode = HIBERNATION_INVALID;
+ if (secure_hibernate(SIG_ENFORCE | SIG_CHECK_SKEY))
+ return -EPERM;
+
p = memchr(buf, '\n', n);
len = p ? p - buf : n;
@@ -367,6 +367,22 @@ static int clean_key_regen_flag(void)
return ret;
}
+bool secure_hibernate(u8 check_items)
+{
+ bool ret = true;
+
+ if (check_items & SIG_ENFORCE)
+ ret = sig_enforce;
+
+ /* check S4 key to lock hibernate when not available */
+ if (ret && (check_items & SIG_CHECK_SKEY))
+ ret = ret && !skey_data_available();
+ if (ret && (check_items & SIG_CHECK_WKEY))
+ ret = ret && (wkey_data_available() != 0);
+
+ return ret;
+}
+
static int __init init_sign_key_data(void)
{
skey_data = (void *)get_zeroed_page(GFP_KERNEL);
@@ -15,6 +15,7 @@
#include <linux/workqueue.h>
#include <linux/debugfs.h>
#include <linux/seq_file.h>
+#include <linux/efi.h>
#include "power.h"
@@ -301,7 +302,11 @@ static ssize_t state_show(struct kobject *kobj, struct kobj_attribute *attr,
}
#endif
#ifdef CONFIG_HIBERNATION
- s += sprintf(s, "%s\n", "disk");
+ if (secure_hibernate(SIG_ENFORCE | SIG_CHECK_SKEY)) {
+ s += sprintf(s, "\n");
+ } else {
+ s += sprintf(s, "%s\n", "disk");
+ }
#else
if (s != buf)
/* convert the last space to a newline */
@@ -2,6 +2,7 @@
#include <linux/suspend_ioctls.h>
#include <linux/utsname.h>
#include <linux/freezer.h>
+#include <linux/module.h>
/* The maximum length of snapshot signature */
#define SIG_LEN 512
@@ -174,6 +175,11 @@ extern int swsusp_unmark(void);
#endif
/* kernel/power/hibernate_key.c */
+#define SIG_ENFORCE (1<<0) /* Check sig_enforce flag */
+#define SIG_CHECK_SKEY (1<<1) /* Check S4SignKey exist */
+#define SIG_CHECK_WKEY (1<<2) /* Check S4WakeKey exist */
+#define SIG_SECURE_LOCKDOWN (1<<3) /* TODO: binding to new secure level */
+
#ifdef CONFIG_SNAPSHOT_VERIFICATION
extern bool skey_data_available(void);
extern struct key *get_sign_key(void);
@@ -189,6 +195,7 @@ 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);
extern int set_key_regen_flag(void);
+extern bool secure_hibernate(u8 check_items);
#else
static inline bool skey_data_available(void)
{
@@ -207,6 +214,12 @@ static inline int set_key_regen_flag(void)
{
return 0;
}
+static inline bool secure_hibernate(u8 check_items)
+{
+ /* TODO: adapt to kernel lockdown */
+
+ return false;
+}
#endif /* !CONFIG_SNAPSHOT_VERIFICATION */
/* kernel/power/block_io.c */
@@ -1759,13 +1759,15 @@ asmlinkage int swsusp_save(void)
nr_copy_pages = nr_pages;
nr_meta_pages = DIV_ROUND_UP(nr_pages * sizeof(long), PAGE_SIZE);
- if (skey_data_available()) {
- ret = swsusp_generate_signature(©_bm, nr_pages);
- if (ret)
- return ret;
- } else
- /* set zero signature if skey doesn't exist */
- memset(signature, 0, SIG_LEN);
+ if (secure_hibernate(0)) {
+ if (skey_data_available()) {
+ ret = swsusp_generate_signature(©_bm, nr_pages);
+ if (ret)
+ return ret;
+ } else
+ /* set zero signature if skey doesn't exist */
+ memset(signature, 0, SIG_LEN);
+ }
printk(KERN_INFO "PM: Hibernation image created (%d pages copied)\n",
nr_pages);
@@ -2398,14 +2400,15 @@ int snapshot_write_next(struct snapshot_handle *handle)
if (error)
return error;
-#ifdef CONFIG_SNAPSHOT_VERIFICATION
/* Allocate void * array to keep buffer point for generate hash,
* handle_buffers will freed in snapshot_image_verify().
*/
- handle_buffers = kmalloc(sizeof(void *) * nr_copy_pages, GFP_KERNEL);
- if (!handle_buffers)
- pr_err("Allocate hash buffer fail!\n");
-#endif
+ if (secure_hibernate(0)) {
+ handle_buffers =
+ kmalloc(sizeof(void *) * nr_copy_pages, GFP_KERNEL);
+ if (!handle_buffers)
+ pr_err("Allocate hash buffer fail!\n");
+ }
error = memory_bm_create(©_bm, GFP_ATOMIC, PG_ANY);
if (error)
@@ -2433,10 +2436,8 @@ int snapshot_write_next(struct snapshot_handle *handle)
handle->sync_read = 0;
if (IS_ERR(handle->buffer))
return PTR_ERR(handle->buffer);
-#ifdef CONFIG_SNAPSHOT_VERIFICATION
- if (handle_buffers)
+ if (secure_hibernate(0) && handle_buffers)
*handle_buffers = handle->buffer;
-#endif
}
} else {
copy_last_highmem_page();
@@ -2447,13 +2448,15 @@ int snapshot_write_next(struct snapshot_handle *handle)
return PTR_ERR(handle->buffer);
if (handle->buffer != buffer)
handle->sync_read = 0;
-#ifdef CONFIG_SNAPSHOT_VERIFICATION
- if (handle_buffers)
- *(handle_buffers + (handle->cur - nr_meta_pages - 1)) = handle->buffer;
- /* Keep the buffer of sign key in snapshot */
- if (pfn == sig_forward_info_pfn)
- sig_forward_info_buf = handle->buffer;
-#endif
+ if (secure_hibernate(0)) {
+ if (handle_buffers) {
+ unsigned int offset = handle->cur - nr_meta_pages - 1;
+ *(handle_buffers + offset) = handle->buffer;
+ }
+ /* Keep the buffer of sign key in snapshot */
+ if (pfn == sig_forward_info_pfn)
+ sig_forward_info_buf = handle->buffer;
+ }
}
handle->cur++;
return PAGE_SIZE;
@@ -2546,7 +2549,7 @@ int snapshot_image_verify(void)
{
struct timeval start;
struct timeval stop;
- struct crypto_shash *tfm = NULL
+ struct crypto_shash *tfm = NULL;
struct shash_desc *desc;
u8 *digest = NULL;
size_t digest_size, desc_size;
@@ -1004,7 +1004,7 @@ static int load_image(struct swap_map_handle *handle,
snapshot_write_finalize(snapshot);
if (!snapshot_image_loaded(snapshot))
ret = -ENODATA;
- else
+ else if (secure_hibernate(0))
ret = snapshot_image_verify();
}
swsusp_show_speed(&start, &stop, nr_to_read, "Read");
@@ -1360,7 +1360,7 @@ out_finish:
}
}
}
- if (!ret)
+ if (!ret && secure_hibernate(0))
ret = snapshot_image_verify();
}
swsusp_show_speed(&start, &stop, nr_to_read, "Read");
@@ -48,6 +48,9 @@ static int snapshot_open(struct inode *inode, struct file *filp)
struct snapshot_data *data;
int error;
+ if (secure_hibernate(SIG_ENFORCE | SIG_CHECK_SKEY | SIG_SECURE_LOCKDOWN))
+ return -EPERM;
+
lock_system_sleep();
if (!atomic_add_unless(&snapshot_device_available, -1, 0)) {
@@ -254,7 +257,7 @@ static long snapshot_ioctl(struct file *filp, unsigned int cmd,
error = -EPERM;
break;
}
- if (snapshot_image_verify()) {
+ if (secure_hibernate(0) && snapshot_image_verify()) {
error = -EPERM;
break;
}