[RFC v1] kexec: Prototype for signature verification within Xen

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

 



These changes work in conjunction with the signature
verification support for Xen I published recently.

Prior to this change, kexec supported the following
three modes of operation:

kexec_load:
- unverified loading of kernel into Linux (original mode)
- unverified loading of kernel into Xen
kexec_file_load (the -s option to kexec):
- verified loading of kernel into Linux

With the verified loading of a kernel into Linux, the scope
of kexec changed drastically as the kernel performs most of
the work that kexec previously did; the kernel does so so as
to reduce the risk of compromise.

For example, the unverified loading of a kernel into Linux
involves locating memory within the system to load the
various pieces of data (kernel, initramdisk, command line)
as well as reserving additional memory such as the first 1MB
on x86 for legacy reasons as well as something known as
'purgatory', a trampoline that checks the integrity of the
contents of loaded pieces of data, before invoking that
loaded kernel. The management of purgatory involves
manipulating an embedded ELF purgatory object file to insert
a memory hash value, and rewrite a few run-time switches
based on kexec command line parameters.

By contrast, the verified loading essentially just passes
file handles for the kernel, initramdisk, and command line
pointer, and the kernel takes care of the rest, by
performing all the work that the unverified kexec load would
do, but inside the kernel using trusted kernel code.

This changeset adds a fourth mode to kexec:

- verified loading of kernel into Xen

In general, Xen performs the signature verification on the
loaded kernel, much as Linux does, but that is where the
similarities end.  In the current Xen implementation, no
infrastructure is present to support reading from [Linux
dom0] file handles, or for manipulating ELF objects. As
such, without Xen support for these actions, Xen relies upon
kexec to provide these services, which is what this mode
does.

To achieve this, this mode of operation essentially vectors
the verified load for Xen through the non-verified path,
which performs all the needed actions for kexec to work, but
then makes an adjustment to pass the entire kernel file, not
just the loadable portion of the kernel file, to Xen in
order to provide the proper image for signature
verification.

The loading of kexec images for signature verification for
Xen is indicated with the -s switch, just like for Linux.

Changes to configure.ac are for detecting whether or not the
Xen version supports this kexec_file_load hypercall op.

Changes to kexec-bzImage64.c are for recording what the
change to the kernel image entry needs to be (the entire
kernel file, not just the loadable portion), as well as
vectoring kexec_file_load through kexec_load for Xen.

Changes to kexec-xen.c are to invoke the new Xen
kexec_file_load hypercall op, from kexec_load.

Changes to kexec.c are to vector kexec_file_load for Xen
throgh kexec_load for Xen, as well as make the correction
for passing the complete kernel file to Xen.

Signed-off-by: Eric DeVolder <eric.devolder@xxxxxxxxxx>
---
 configure.ac                        |  8 ++++++++
 kexec/arch/x86_64/kexec-bzImage64.c | 18 ++++++++++++++++++
 kexec/kexec-xen.c                   |  7 +++++++
 kexec/kexec.c                       | 38 +++++++++++++++++++++++++++++++++++++
 4 files changed, 71 insertions(+)

diff --git a/configure.ac b/configure.ac
index e05d601..a11787b 100644
--- a/configure.ac
+++ b/configure.ac
@@ -190,6 +190,14 @@ if test "$ac_cv_lib_xenctrl_xc_kexec_load" = yes ; then
 		AC_MSG_NOTICE([The kexec_status call is not available]))
 fi
 
+dnl Check for the Xen kexec_status hypercall - reachable from --with-xen=yes|dl
+if test "$ac_cv_lib_xenctrl_xc_kexec_load" = yes ; then
+	AC_CHECK_LIB(xenctrl, xc_kexec_file_load,
+		AC_DEFINE(HAVE_XEN_KEXEC_FILE_LOAD, 1,
+			[The Xen kexec_file_load call is available]),
+		AC_MSG_NOTICE([The Xen kexec_file_load call is not available]))
+fi
+
 dnl ---Sanity checks
 if test "$CC"      = "no"; then AC_MSG_ERROR([cc not found]); fi
 if test "$CPP"     = "no"; then AC_MSG_ERROR([cpp not found]); fi
diff --git a/kexec/arch/x86_64/kexec-bzImage64.c b/kexec/arch/x86_64/kexec-bzImage64.c
index 8edb3e4..98e9d50 100644
--- a/kexec/arch/x86_64/kexec-bzImage64.c
+++ b/kexec/arch/x86_64/kexec-bzImage64.c
@@ -207,6 +207,20 @@ static int do_bzImage64_load(struct kexec_info *info,
 	align = real_mode->kernel_alignment;
 	addr = add_buffer(info, kernel + kern16_size, k_size,
 			  size, align, 0x100000, -1, -1);
+#ifdef HAVE_XEN_KEXEC_FILE_LOAD
+    if (xen_present() && info->file_mode)
+    {
+        /* Record info for post-purgatory hash computation replacement with kernel file */
+        extern char *original_kernel;
+        extern off_t original_kernel_len;
+        extern char *replacement_kernel;
+        extern off_t replacement_kernel_len;
+        original_kernel = kernel + kern16_size;
+        original_kernel_len = k_size;
+        replacement_kernel = kernel;
+        replacement_kernel_len = kernel_len;
+    }
+#endif
 	if (addr == ULONG_MAX)
 		die("can not load bzImage64");
 	dbgprintf("Loaded 64bit kernel at 0x%lx\n", addr);
@@ -330,7 +344,11 @@ int bzImage64_load(int argc, char **argv, const char *buf, off_t len,
 	int opt;
 	int result;
 
+#ifdef HAVE_XEN_KEXEC_FILE_LOAD
+	if (info->file_mode && !xen_present())
+#else
 	if (info->file_mode)
+#endif
 		return bzImage64_load_file(argc, argv, info);
 
 	/* See options.h -- add any more there, too. */
diff --git a/kexec/kexec-xen.c b/kexec/kexec-xen.c
index 1887390..2a59d62 100644
--- a/kexec/kexec-xen.c
+++ b/kexec/kexec-xen.c
@@ -124,8 +124,15 @@ int xen_kexec_load(struct kexec_info *info)
 		arch = EM_386;
 #endif
 
+#ifdef HAVE_XEN_KEXEC_FILE_LOAD
+    if (info->file_mode)
+	ret = xc_kexec_file_load(xch, type, arch, (uint64_t)info->entry,
+			    nr_segments, xen_segs);
+    else
+#else
 	ret = xc_kexec_load(xch, type, arch, (uint64_t)info->entry,
 			    nr_segments, xen_segs);
+#endif
 
 out:
 	xc_hypercall_buffer_array_destroy(xch, array);
diff --git a/kexec/kexec.c b/kexec/kexec.c
index 32ae56c..80a4905 100644
--- a/kexec/kexec.c
+++ b/kexec/kexec.c
@@ -61,6 +61,13 @@ static unsigned long kexec_flags = 0;
 /* Flags for kexec file (fd) based syscall */
 static unsigned long kexec_file_flags = 0;
 int kexec_debug = 0;
+#ifdef HAVE_XEN_KEXEC_FILE_LOAD
+static int do_kexec_file_syscall = 0;
+char *original_kernel = NULL;
+off_t original_kernel_len = 0;
+char *replacement_kernel = NULL;
+off_t replacement_kernel_len = 0;
+#endif
 
 void dbgprint_mem_range(const char *prefix, struct memory_range *mr, int nr_mr)
 {
@@ -690,6 +697,24 @@ static void update_purgatory(struct kexec_info *info)
 			   sizeof(region));
 	elf_rel_set_symbol(&info->rhdr, "sha256_digest", &digest,
 			   sizeof(digest));
+#ifdef HAVE_XEN_KEXEC_FILE_LOAD
+    /* Now that purgatory hash computed, replace kernel loadable segment with entire kernel file for Xen to process */
+    if (xen_present() && info->file_mode)
+    {
+        if (original_kernel && original_kernel_len)
+        {
+	        for(i = 0; i < info->nr_segments; i++) {
+                if ((info->segment[i].buf == original_kernel) &&
+                    (info->segment[i].bufsz == original_kernel_len))
+                {
+                    info->segment[i].buf = replacement_kernel;
+                    info->segment[i].bufsz = replacement_kernel_len;
+                    break;
+                }
+            }
+        }
+    }
+#endif
 }
 
 /*
@@ -710,6 +735,9 @@ static int my_load(const char *type, int fileind, int argc, char **argv,
 	memset(&info, 0, sizeof(info));
 	info.kexec_flags = kexec_flags;
 	info.skip_checks = skip_checks;
+#ifdef HAVE_XEN_KEXEC_FILE_LOAD
+	info.file_mode = do_kexec_file_syscall;
+#endif
 
 	result = 0;
 	if (argc - fileind <= 0) {
@@ -1260,7 +1288,9 @@ int main(int argc, char *argv[])
 	int do_ifdown = 0, skip_ifdown = 0;
 	int do_unload = 0;
 	int do_reuse_initrd = 0;
+#ifndef HAVE_XEN_KEXEC_FILE_LOAD
 	int do_kexec_file_syscall = 0;
+#endif
 	int do_kexec_fallback = 0;
 	int skip_checks = 0;
 	int do_status = 0;
@@ -1489,7 +1519,11 @@ int main(int argc, char *argv[])
 			result = k_unload(kexec_flags);
 	}
 	if (do_load && (result == 0)) {
+#ifdef HAVE_XEN_KEXEC_FILE_LOAD
+		if (do_kexec_file_syscall && !xen_present()) {
+#else
 		if (do_kexec_file_syscall) {
+#endif
 			result = do_kexec_file_load(fileind, argc, argv,
 						 kexec_file_flags);
 			if (do_kexec_fallback) switch (result) {
@@ -1533,7 +1567,11 @@ int main(int argc, char *argv[])
 					break;
 			}
 		}
+#ifdef HAVE_XEN_KEXEC_FILE_LOAD
+		if (!do_kexec_file_syscall || xen_present())
+#else
 		if (!do_kexec_file_syscall)
+#endif
 			result = my_load(type, fileind, argc, argv,
 						kexec_flags, skip_checks, entry);
 	}
-- 
2.7.4


_______________________________________________
kexec mailing list
kexec@xxxxxxxxxxxxxxxxxxx
http://lists.infradead.org/mailman/listinfo/kexec



[Index of Archives]     [LM Sensors]     [Linux Sound]     [ALSA Users]     [ALSA Devel]     [Linux Audio Users]     [Linux Media]     [Kernel]     [Gimp]     [Yosemite News]     [Linux Media]

  Powered by Linux