diff mbox

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

Message ID 1377169317-5959-16-git-send-email-jlee@suse.com (mailing list archive)
State RFC, archived
Headers show

Commit Message

Chun-Yi Lee Aug. 22, 2013, 11:01 a.m. UTC
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@suse.cz>
Signed-off-by: Lee, Chun-Yi <jlee@suse.com>
---
 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(-)

Comments

Pavel Machek Aug. 25, 2013, 4:42 p.m. UTC | #1
On Thu 2013-08-22 19:01:54, Lee, Chun-Yi wrote:
> 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@suse.cz>
> Signed-off-by: Lee, Chun-Yi <jlee@suse.com>
> ---
>  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;
>  

You clearly need some helper function.
									Pavel
joeyli Aug. 27, 2013, 10:14 a.m. UTC | #2
? ??2013-08-25 ? 18:42 +0200?Pavel Machek ???
> On Thu 2013-08-22 19:01:54, Lee, Chun-Yi wrote:
> > 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@suse.cz>
> > Signed-off-by: Lee, Chun-Yi <jlee@suse.com>
> > ---
> >  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;
> >  
> 
> You clearly need some helper function.
> 									Pavel
> 

I will use a help function to replace those ifdef block.

Thanks for your suggestion!
Joey Lee

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

Patch

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;