198 lines
		
	
	
		
			6.0 KiB
		
	
	
	
		
			Diff
		
	
	
	
	
	
			
		
		
	
	
			198 lines
		
	
	
		
			6.0 KiB
		
	
	
	
		
			Diff
		
	
	
	
	
	
From e4c62c12635a371e43bd17e8d33a936668264491 Mon Sep 17 00:00:00 2001
 | 
						|
From: Dave Howells <dhowells@redhat.com>
 | 
						|
Date: Fri, 5 May 2017 08:21:58 +0100
 | 
						|
Subject: [PATCH 2/4] efi: Add an EFI signature blob parser
 | 
						|
 | 
						|
Add a function to parse an EFI signature blob looking for elements of
 | 
						|
interest.  A list is made up of a series of sublists, where all the
 | 
						|
elements in a sublist are of the same type, but sublists can be of
 | 
						|
different types.
 | 
						|
 | 
						|
For each sublist encountered, the function pointed to by the
 | 
						|
get_handler_for_guid argument is called with the type specifier GUID and
 | 
						|
returns either a pointer to a function to handle elements of that type or
 | 
						|
NULL if the type is not of interest.
 | 
						|
 | 
						|
If the sublist is of interest, each element is passed to the handler
 | 
						|
function in turn.
 | 
						|
 | 
						|
Signed-off-by: David Howells <dhowells@redhat.com>
 | 
						|
---
 | 
						|
 certs/Kconfig       |   8 ++++
 | 
						|
 certs/Makefile      |   1 +
 | 
						|
 certs/efi_parser.c  | 112 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 | 
						|
 include/linux/efi.h |   9 +++++
 | 
						|
 4 files changed, 130 insertions(+)
 | 
						|
 create mode 100644 certs/efi_parser.c
 | 
						|
 | 
						|
diff --git a/certs/Kconfig b/certs/Kconfig
 | 
						|
index 6ce51ed..630ae09 100644
 | 
						|
--- a/certs/Kconfig
 | 
						|
+++ b/certs/Kconfig
 | 
						|
@@ -82,4 +82,12 @@ config SYSTEM_BLACKLIST_HASH_LIST
 | 
						|
 	  wrapper to incorporate the list into the kernel.  Each <hash> should
 | 
						|
 	  be a string of hex digits.
 | 
						|
 | 
						|
+config EFI_SIGNATURE_LIST_PARSER
 | 
						|
+	bool "EFI signature list parser"
 | 
						|
+	depends on EFI
 | 
						|
+	select X509_CERTIFICATE_PARSER
 | 
						|
+	help
 | 
						|
+	  This option provides support for parsing EFI signature lists for
 | 
						|
+	  X.509 certificates and turning them into keys.
 | 
						|
+
 | 
						|
 endmenu
 | 
						|
diff --git a/certs/Makefile b/certs/Makefile
 | 
						|
index 4119bb3..738151a 100644
 | 
						|
--- a/certs/Makefile
 | 
						|
+++ b/certs/Makefile
 | 
						|
@@ -9,6 +9,7 @@ obj-$(CONFIG_SYSTEM_BLACKLIST_KEYRING) += blacklist_hashes.o
 | 
						|
 else
 | 
						|
 obj-$(CONFIG_SYSTEM_BLACKLIST_KEYRING) += blacklist_nohashes.o
 | 
						|
 endif
 | 
						|
+obj-$(CONFIG_EFI_SIGNATURE_LIST_PARSER) += efi_parser.o
 | 
						|
 | 
						|
 ifeq ($(CONFIG_SYSTEM_TRUSTED_KEYRING),y)
 | 
						|
 | 
						|
diff --git a/certs/efi_parser.c b/certs/efi_parser.c
 | 
						|
new file mode 100644
 | 
						|
index 0000000..4e396f9
 | 
						|
--- /dev/null
 | 
						|
+++ b/certs/efi_parser.c
 | 
						|
@@ -0,0 +1,112 @@
 | 
						|
+/* EFI signature/key/certificate list parser
 | 
						|
+ *
 | 
						|
+ * Copyright (C) 2012, 2016 Red Hat, Inc. All Rights Reserved.
 | 
						|
+ * Written by David Howells (dhowells@redhat.com)
 | 
						|
+ *
 | 
						|
+ * This program is free software; you can redistribute it and/or
 | 
						|
+ * modify it under the terms of the GNU General Public Licence
 | 
						|
+ * as published by the Free Software Foundation; either version
 | 
						|
+ * 2 of the Licence, or (at your option) any later version.
 | 
						|
+ */
 | 
						|
+
 | 
						|
+#define pr_fmt(fmt) "EFI: "fmt
 | 
						|
+#include <linux/module.h>
 | 
						|
+#include <linux/printk.h>
 | 
						|
+#include <linux/err.h>
 | 
						|
+#include <linux/efi.h>
 | 
						|
+
 | 
						|
+/**
 | 
						|
+ * parse_efi_signature_list - Parse an EFI signature list for certificates
 | 
						|
+ * @source: The source of the key
 | 
						|
+ * @data: The data blob to parse
 | 
						|
+ * @size: The size of the data blob
 | 
						|
+ * @get_handler_for_guid: Get the handler func for the sig type (or NULL)
 | 
						|
+ *
 | 
						|
+ * Parse an EFI signature list looking for elements of interest.  A list is
 | 
						|
+ * made up of a series of sublists, where all the elements in a sublist are of
 | 
						|
+ * the same type, but sublists can be of different types.
 | 
						|
+ *
 | 
						|
+ * For each sublist encountered, the @get_handler_for_guid function is called
 | 
						|
+ * with the type specifier GUID and returns either a pointer to a function to
 | 
						|
+ * handle elements of that type or NULL if the type is not of interest.
 | 
						|
+ *
 | 
						|
+ * If the sublist is of interest, each element is passed to the handler
 | 
						|
+ * function in turn.
 | 
						|
+ *
 | 
						|
+ * Error EBADMSG is returned if the list doesn't parse correctly and 0 is
 | 
						|
+ * returned if the list was parsed correctly.  No error can be returned from
 | 
						|
+ * the @get_handler_for_guid function or the element handler function it
 | 
						|
+ * returns.
 | 
						|
+ */
 | 
						|
+int __init parse_efi_signature_list(
 | 
						|
+	const char *source,
 | 
						|
+	const void *data, size_t size,
 | 
						|
+	efi_element_handler_t (*get_handler_for_guid)(const efi_guid_t *))
 | 
						|
+{
 | 
						|
+	efi_element_handler_t handler;
 | 
						|
+	unsigned offs = 0;
 | 
						|
+
 | 
						|
+	pr_devel("-->%s(,%zu)\n", __func__, size);
 | 
						|
+
 | 
						|
+	while (size > 0) {
 | 
						|
+		const efi_signature_data_t *elem;
 | 
						|
+		efi_signature_list_t list;
 | 
						|
+		size_t lsize, esize, hsize, elsize;
 | 
						|
+
 | 
						|
+		if (size < sizeof(list))
 | 
						|
+			return -EBADMSG;
 | 
						|
+
 | 
						|
+		memcpy(&list, data, sizeof(list));
 | 
						|
+		pr_devel("LIST[%04x] guid=%pUl ls=%x hs=%x ss=%x\n",
 | 
						|
+			 offs,
 | 
						|
+			 list.signature_type.b, list.signature_list_size,
 | 
						|
+			 list.signature_header_size, list.signature_size);
 | 
						|
+
 | 
						|
+		lsize = list.signature_list_size;
 | 
						|
+		hsize = list.signature_header_size;
 | 
						|
+		esize = list.signature_size;
 | 
						|
+		elsize = lsize - sizeof(list) - hsize;
 | 
						|
+
 | 
						|
+		if (lsize > size) {
 | 
						|
+			pr_devel("<--%s() = -EBADMSG [overrun @%x]\n",
 | 
						|
+				 __func__, offs);
 | 
						|
+			return -EBADMSG;
 | 
						|
+		}
 | 
						|
+
 | 
						|
+		if (lsize < sizeof(list) ||
 | 
						|
+		    lsize - sizeof(list) < hsize ||
 | 
						|
+		    esize < sizeof(*elem) ||
 | 
						|
+		    elsize < esize ||
 | 
						|
+		    elsize % esize != 0) {
 | 
						|
+			pr_devel("- bad size combo @%x\n", offs);
 | 
						|
+			return -EBADMSG;
 | 
						|
+		}
 | 
						|
+
 | 
						|
+		handler = get_handler_for_guid(&list.signature_type);
 | 
						|
+		if (!handler) {
 | 
						|
+			data += lsize;
 | 
						|
+			size -= lsize;
 | 
						|
+			offs += lsize;
 | 
						|
+			continue;
 | 
						|
+		}
 | 
						|
+
 | 
						|
+		data += sizeof(list) + hsize;
 | 
						|
+		size -= sizeof(list) + hsize;
 | 
						|
+		offs += sizeof(list) + hsize;
 | 
						|
+
 | 
						|
+		for (; elsize > 0; elsize -= esize) {
 | 
						|
+			elem = data;
 | 
						|
+
 | 
						|
+			pr_devel("ELEM[%04x]\n", offs);
 | 
						|
+			handler(source,
 | 
						|
+				&elem->signature_data,
 | 
						|
+				esize - sizeof(*elem));
 | 
						|
+
 | 
						|
+			data += esize;
 | 
						|
+			size -= esize;
 | 
						|
+			offs += esize;
 | 
						|
+		}
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	return 0;
 | 
						|
+}
 | 
						|
diff --git a/include/linux/efi.h b/include/linux/efi.h
 | 
						|
index 3259ad6..08024c6 100644
 | 
						|
--- a/include/linux/efi.h
 | 
						|
+++ b/include/linux/efi.h
 | 
						|
@@ -1055,6 +1055,15 @@ extern int efi_memattr_apply_permissions(struct mm_struct *mm,
 | 
						|
 char * __init efi_md_typeattr_format(char *buf, size_t size,
 | 
						|
 				     const efi_memory_desc_t *md);
 | 
						|
 | 
						|
+
 | 
						|
+typedef void (*efi_element_handler_t)(const char *source,
 | 
						|
+				      const void *element_data,
 | 
						|
+				      size_t element_size);
 | 
						|
+extern int __init parse_efi_signature_list(
 | 
						|
+	const char *source,
 | 
						|
+	const void *data, size_t size,
 | 
						|
+	efi_element_handler_t (*get_handler_for_guid)(const efi_guid_t *));
 | 
						|
+
 | 
						|
 /**
 | 
						|
  * efi_range_is_wc - check the WC bit on an address range
 | 
						|
  * @start: starting kvirt address
 | 
						|
-- 
 | 
						|
2.9.3
 | 
						|
 |