[PATCH 15/18] Hibernate: adapt to UEFI secure boot with signature check

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



In current solution, the snapshot signature check used the RSA key-pair
that are generated by bootloader(e.g. shim) and pass the key-pair to
kernel through EFI variables. I choice to binding the snapshot
signature check mechanism with UEFI secure boot for provide stronger
protection of hibernate. Current behavior is following:

 + UEFI Secure Boot ON, Kernel found key-pair from shim:
   Will do the S4 signature check.

 + UEFI Secure Boot ON, Kernel didn't find key-pair from shim:
   Will lock down S4 function.

 + UEFI Secure Boot OFF
   Will NOT do the S4 signature check.
   Ignore any keys from bootloader.

v2:
Replace sign_key_data_loaded() by skey_data_available() to check sign key data
is available for hibernate.

Reviewed-by: Jiri Kosina <jkosina@xxxxxxx>
Signed-off-by: Lee, Chun-Yi <jlee@xxxxxxxx>
---
 kernel/power/hibernate.c |   36 +++++++++++++++++-
 kernel/power/main.c      |   11 +++++-
 kernel/power/snapshot.c  |   95 ++++++++++++++++++++++++++--------------------
 kernel/power/swap.c      |    4 +-
 kernel/power/user.c      |   11 +++++
 5 files changed, 112 insertions(+), 45 deletions(-)

diff --git a/kernel/power/hibernate.c b/kernel/power/hibernate.c
index c545b15..0f19f3d 100644
--- a/kernel/power/hibernate.c
+++ b/kernel/power/hibernate.c
@@ -29,6 +29,7 @@
 #include <linux/ctype.h>
 #include <linux/genhd.h>
 #include <linux/key.h>
+#include <linux/efi.h>
 
 #include "power.h"
 
@@ -632,7 +633,14 @@ static void power_down(void)
 int hibernate(void)
 {
 	int error;
-	int skey_error;
+
+#ifdef CONFIG_SNAPSHOT_VERIFICATION
+	if (!capable(CAP_COMPROMISE_KERNEL) && !skey_data_available()) {
+#else
+	if (!capable(CAP_COMPROMISE_KERNEL)) {
+#endif
+		return -EPERM;
+	}
 
 	lock_system_sleep();
 	/* The snapshot device should not be opened while we're running */
@@ -799,6 +807,15 @@ static int software_resume(void)
 	if (error)
 		goto Unlock;
 
+#ifdef CONFIG_SNAPSHOT_VERIFICATION
+	if (!capable(CAP_COMPROMISE_KERNEL) && !wkey_data_available()) {
+#else
+	if (!capable(CAP_COMPROMISE_KERNEL)) {
+#endif
+		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;
@@ -892,6 +909,15 @@ static ssize_t disk_show(struct kobject *kobj, struct kobj_attribute *attr,
 	int i;
 	char *start = buf;
 
+#ifdef CONFIG_SNAPSHOT_VERIFICATION
+	if (efi_enabled(EFI_SECURE_BOOT) && !skey_data_available()) {
+#else
+	if (efi_enabled(EFI_SECURE_BOOT)) {
+#endif
+		buf += sprintf(buf, "[%s]\n", "disabled");
+		return buf-start;
+	}
+
 	for (i = HIBERNATION_FIRST; i <= HIBERNATION_MAX; i++) {
 		if (!hibernation_modes[i])
 			continue;
@@ -926,6 +952,14 @@ static ssize_t disk_store(struct kobject *kobj, struct kobj_attribute *attr,
 	char *p;
 	int mode = HIBERNATION_INVALID;
 
+#ifdef CONFIG_SNAPSHOT_VERIFICATION
+	if (!capable(CAP_COMPROMISE_KERNEL) && !skey_data_available()) {
+#else
+	if (!capable(CAP_COMPROMISE_KERNEL)) {
+#endif
+		return -EPERM;
+	}
+
 	p = memchr(buf, '\n', n);
 	len = p ? p - buf : n;
 
diff --git a/kernel/power/main.c b/kernel/power/main.c
index 1d1bf63..47bf300 100644
--- a/kernel/power/main.c
+++ b/kernel/power/main.c
@@ -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,15 @@ static ssize_t state_show(struct kobject *kobj, struct kobj_attribute *attr,
 	}
 #endif
 #ifdef CONFIG_HIBERNATION
-	s += sprintf(s, "%s\n", "disk");
+	if (!efi_enabled(EFI_SECURE_BOOT)) {
+		s += sprintf(s, "%s\n", "disk");
+#ifdef CONFIG_SNAPSHOT_VERIFICATION
+	} else if (skey_data_available()) {
+		s += sprintf(s, "%s\n", "disk");
+#endif
+	} else {
+		s += sprintf(s, "\n");
+	}
 #else
 	if (s != buf)
 		/* convert the last space to a newline */
diff --git a/kernel/power/snapshot.c b/kernel/power/snapshot.c
index cf3d69c..36c7157 100644
--- a/kernel/power/snapshot.c
+++ b/kernel/power/snapshot.c
@@ -860,7 +860,8 @@ static struct page *saveable_highmem_page(struct zone *zone, unsigned long pfn)
 
 	BUG_ON(!PageHighMem(page));
 
-	if (swsusp_page_is_sign_key(page))
+	if (!capable(CAP_COMPROMISE_KERNEL) &&
+	    swsusp_page_is_sign_key(page))
 		return NULL;
 
 	if (swsusp_page_is_forbidden(page) ||  swsusp_page_is_free(page) ||
@@ -925,7 +926,8 @@ static struct page *saveable_page(struct zone *zone, unsigned long pfn)
 
 	BUG_ON(PageHighMem(page));
 
-	if (swsusp_page_is_sign_key(page))
+	if (!capable(CAP_COMPROMISE_KERNEL) &&
+	    swsusp_page_is_sign_key(page))
 		return NULL;
 
 	if (swsusp_page_is_forbidden(page) || swsusp_page_is_free(page))
@@ -1056,35 +1058,37 @@ copy_data_pages(struct memory_bitmap *copy_bm, struct memory_bitmap *orig_bm)
 #ifdef CONFIG_SNAPSHOT_VERIFICATION
 	struct page *d_page;
 	void *hash_buffer = NULL;
-	struct crypto_shash *tfm;
-	struct shash_desc *desc;
-	u8 *digest;
+	struct crypto_shash *tfm = NULL;
+	struct shash_desc *desc = NULL;
+	u8 *digest = NULL;
 	size_t digest_size, desc_size;
 	struct key *s4_sign_key;
 	struct public_key_signature *pks;
 	int ret;
 
 	ret = -ENOMEM;
-	tfm = crypto_alloc_shash(SNAPSHOT_HASH, 0, 0);
-	if (IS_ERR(tfm)) {
-		pr_err("IS_ERR(tfm): %ld", PTR_ERR(tfm));
-		return PTR_ERR(tfm);
-	}
+	if (!capable(CAP_COMPROMISE_KERNEL)) {
+		tfm = crypto_alloc_shash(SNAPSHOT_HASH, 0, 0);
+		if (IS_ERR(tfm)) {
+			pr_err("IS_ERR(tfm): %ld", PTR_ERR(tfm));
+			return PTR_ERR(tfm);
+		}
 
-	desc_size = crypto_shash_descsize(tfm) + sizeof(*desc);
-	digest_size = crypto_shash_digestsize(tfm);
-	digest = kzalloc(digest_size + desc_size, GFP_KERNEL);
-	if (!digest) {
-		pr_err("digest allocate fail");
-		ret = -ENOMEM;
-		goto error_digest;
+		desc_size = crypto_shash_descsize(tfm) + sizeof(*desc);
+		digest_size = crypto_shash_digestsize(tfm);
+		digest = kzalloc(digest_size + desc_size, GFP_KERNEL);
+		if (!digest) {
+			pr_err("digest allocate fail");
+			ret = -ENOMEM;
+			goto error_digest;
+		}
+		desc = (void *) digest + digest_size;
+		desc->tfm = tfm;
+		desc->flags = CRYPTO_TFM_REQ_MAY_SLEEP;
+		ret = crypto_shash_init(desc);
+		if (ret < 0)
+			goto error_shash;
 	}
-	desc = (void *) digest + digest_size;
-	desc->tfm = tfm;
-	desc->flags = CRYPTO_TFM_REQ_MAY_SLEEP;
-	ret = crypto_shash_init(desc);
-	if (ret < 0)
-		goto error_shash;
 #endif /* CONFIG_SNAPSHOT_VERIFICATION */
 
 	for_each_populated_zone(zone) {
@@ -1106,24 +1110,29 @@ copy_data_pages(struct memory_bitmap *copy_bm, struct memory_bitmap *orig_bm)
 		copy_data_page(dst_pfn, pfn);
 
 #ifdef CONFIG_SNAPSHOT_VERIFICATION
-		/* Generate digest */
-		d_page = pfn_to_page(dst_pfn);
-		if (PageHighMem(d_page)) {
-			void *kaddr;
-			kaddr = kmap_atomic(d_page);
-			copy_page(buffer, kaddr);
-			kunmap_atomic(kaddr);
-			hash_buffer = buffer;
-		} else {
-			hash_buffer = page_address(d_page);
+		if (!capable(CAP_COMPROMISE_KERNEL)) {
+			/* Generate digest */
+			d_page = pfn_to_page(dst_pfn);
+			if (PageHighMem(d_page)) {
+				void *kaddr;
+				kaddr = kmap_atomic(d_page);
+				copy_page(buffer, kaddr);
+				kunmap_atomic(kaddr);
+				hash_buffer = buffer;
+			} else {
+				hash_buffer = page_address(d_page);
+			}
+			ret = crypto_shash_update(desc, hash_buffer, PAGE_SIZE);
+			if (ret)
+				goto error_shash;
 		}
-		ret = crypto_shash_update(desc, hash_buffer, PAGE_SIZE);
-		if (ret)
-			goto error_shash;
 #endif
 	}
 
 #ifdef CONFIG_SNAPSHOT_VERIFICATION
+	if (capable(CAP_COMPROMISE_KERNEL))
+		goto skip_sign;
+
 	crypto_shash_final(desc, digest);
 	if (ret)
 		goto error_shash;
@@ -1153,6 +1162,8 @@ copy_data_pages(struct memory_bitmap *copy_bm, struct memory_bitmap *orig_bm)
 	kfree(pks);
 	kfree(digest);
 	crypto_free_shash(tfm);
+
+skip_sign:
 #endif /* CONFIG_SNAPSHOT_VERIFICATION */
 
 	return 0;
@@ -2382,9 +2393,11 @@ int snapshot_write_next(struct snapshot_handle *handle)
 		/* Allocate void * array to keep buffer point for generate hash,
 		 * h_buf will freed in snapshot_image_verify().
 		 */
-		h_buf = kmalloc(sizeof(void *) * nr_copy_pages, GFP_KERNEL);
-		if (!h_buf)
-			pr_err("Allocate hash buffer fail!");
+		if (!capable(CAP_COMPROMISE_KERNEL)) {
+			h_buf = kmalloc(sizeof(void *) * nr_copy_pages, GFP_KERNEL);
+			if (!h_buf)
+				pr_err("Allocate hash buffer fail!");
+		}
 #endif
 
 		error = memory_bm_create(&copy_bm, GFP_ATOMIC, PG_ANY);
@@ -2414,7 +2427,7 @@ int snapshot_write_next(struct snapshot_handle *handle)
 			if (IS_ERR(handle->buffer))
 				return PTR_ERR(handle->buffer);
 #ifdef CONFIG_SNAPSHOT_VERIFICATION
-			if (h_buf)
+			if (!capable(CAP_COMPROMISE_KERNEL) && h_buf)
 				*h_buf = handle->buffer;
 #endif
 		}
@@ -2428,7 +2441,7 @@ int snapshot_write_next(struct snapshot_handle *handle)
 		if (handle->buffer != buffer)
 			handle->sync_read = 0;
 #ifdef CONFIG_SNAPSHOT_VERIFICATION
-		if (h_buf)
+		if (!capable(CAP_COMPROMISE_KERNEL) && h_buf)
 			*(h_buf + (handle->cur - nr_meta_pages - 1)) = handle->buffer;
 		/* Keep the buffer of sign key in snapshot */
 		if (pfn == skey_data_buf_pfn)
diff --git a/kernel/power/swap.c b/kernel/power/swap.c
index b5f8ce1..40225d7 100644
--- a/kernel/power/swap.c
+++ b/kernel/power/swap.c
@@ -1005,7 +1005,7 @@ static int load_image(struct swap_map_handle *handle,
 		if (!snapshot_image_loaded(snapshot))
 			ret = -ENODATA;
 #ifdef CONFIG_SNAPSHOT_VERIFICATION
-		else {
+		else if (!capable(CAP_COMPROMISE_KERNEL)) {
 			ret = snapshot_image_verify();
 			if (ret)
 				pr_info("PM: snapshot signature check FAIL: %d\n", ret);
@@ -1370,7 +1370,7 @@ out_finish:
 			}
 		}
 #ifdef CONFIG_SNAPSHOT_VERIFICATION
-		if (!ret) {
+		if (!ret && !capable(CAP_COMPROMISE_KERNEL)) {
 			ret = snapshot_image_verify();
 			if (ret)
 				pr_info("PM: snapshot signature check FAIL: %d\n", ret);
diff --git a/kernel/power/user.c b/kernel/power/user.c
index 27b21ee..690f148 100644
--- a/kernel/power/user.c
+++ b/kernel/power/user.c
@@ -48,6 +48,14 @@ static int snapshot_open(struct inode *inode, struct file *filp)
 	struct snapshot_data *data;
 	int error;
 
+#ifdef CONFIG_SNAPSHOT_VERIFICATION
+	if (!capable(CAP_COMPROMISE_KERNEL) && !wkey_data_available()) {
+#else
+	if (!capable(CAP_COMPROMISE_KERNEL)) {
+#endif
+		return -EPERM;
+	}
+
 	lock_system_sleep();
 
 	if (!atomic_add_unless(&snapshot_device_available, -1, 0)) {
@@ -255,6 +263,8 @@ static long snapshot_ioctl(struct file *filp, unsigned int cmd,
 			break;
 		}
 #ifdef CONFIG_SNAPSHOT_VERIFICATION
+		if (capable(CAP_COMPROMISE_KERNEL))
+			goto skip_verify;
 		if (!snapshot_image_verify()) {
 			pr_info("PM: snapshot signature check SUCCESS!\n");
 			snapshot_fill_s4_skey();
@@ -263,6 +273,7 @@ static long snapshot_ioctl(struct file *filp, unsigned int cmd,
 			error = -EPERM;
 			break;
 		}
+skip_verify:
 #endif
 		error = hibernation_restore(data->platform_support);
 		break;
-- 
1.6.4.2

--
To unsubscribe from this list: send the line "unsubscribe linux-efi" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html




[Index of Archives]     [Linux ARM Kernel]     [Linux ARM]     [Linux Omap]     [Fedora ARM]     [IETF Annouce]     [Security]     [Bugtraq]     [Linux OMAP]     [Linux MIPS]     [ECOS]     [Asterisk Internet PBX]     [Linux API]

  Powered by Linux