Patchwork [V7,12/13] Support for taking measurements when kernel etc. are passed to Qemu

login
register
mail settings
Submitter Stefan Berger
Date Aug. 10, 2011, 7:29 p.m.
Message ID <20110810193018.029981610@linux.vnet.ibm.com>
Download mbox | patch
Permalink /patch/109458/
State New
Headers show

Comments

Stefan Berger - Aug. 10, 2011, 7:29 p.m.
This patch adds support for hashing the kernel and initrd as well as the
command line parameters in the case that Qemu was provided the -kernel, -initrd
and -apppend command line parameters. The hashes are then passed to SeaBIOS
for logging. Typically SeaBIOS would take those measurements (hashing) but in
the case Qemu gets these command line parameters, Qemu does not see the kernel
file in its unmodified form anymore (it is modified before it is passed
to the firmware interface). Support for measuring multiboot kernel entries is
also added.

This patch relies on the existing firmware mechanism to pass byte arrays
from Qemu to a BIOS, i.e., SeaBIOS. It introduces structures describing the
header and the following content consisting of an array of structures that
hold the measurements and descriptions of the above mentioned items.

Since hashing requires a sha1 algorithm to be available to Qemu, this patch
introduces a dependency on the freebl library for the sha1 algorithm. The
code for accessing the freebl library's sha1 function has been isolated into
its own file and wrapped with the function call qemu_sha1. Attempts to use the
freebl library's SHA1 function directly didn't work due to clashes of
datatypes with matching names defined by freebl and Qemu.

-v7:
  - Support for multiboot kernels

Signed-off-by: Stefan Berger <stefanb@linux.vnet.ibm.com>

---
 Makefile.target |    2 -
 configure       |   23 +++++++++++
 hw/fw_cfg.h     |    2 +
 hw/multiboot.c  |    8 ++++
 hw/pc.c         |   10 +++++
 sha1.c          |   19 +++++++++
 sha1.h          |    9 ++++
 tpm.c           |  108 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 tpm.h           |   33 +++++++++++++++++
 9 files changed, 213 insertions(+), 1 deletion(-)

Patch

Index: qemu-git/hw/pc.c
===================================================================
--- qemu-git.orig/hw/pc.c
+++ qemu-git/hw/pc.c
@@ -678,6 +678,9 @@  static void load_linux(void *fw_cfg,
 	exit(1);
     }
 
+    tpm_measure_start();
+    tpm_measure_file(kernel_filename, TPM_MSR_TYPE_KERNEL, 8);
+
     /* kernel protocol version */
 #if 0
     fprintf(stderr, "header magic: %#x\n", ldl_p(header+0x202));
@@ -735,6 +738,9 @@  static void load_linux(void *fw_cfg,
                      (uint8_t*)strdup(kernel_cmdline),
                      strlen(kernel_cmdline)+1);
 
+    tpm_measure_buffer(kernel_cmdline, strlen(kernel_cmdline),
+                       TPM_MSR_TYPE_KERNEL_CMDLINE, 8, NULL, 0);
+
     if (protocol >= 0x202) {
 	stl_p(header+0x228, cmdline_addr);
     } else {
@@ -796,9 +802,13 @@  static void load_linux(void *fw_cfg,
         fw_cfg_add_i32(fw_cfg, FW_CFG_INITRD_SIZE, initrd_size);
         fw_cfg_add_bytes(fw_cfg, FW_CFG_INITRD_DATA, initrd_data, initrd_size);
 
+        tpm_measure_buffer(initrd_data, initrd_size, TPM_MSR_TYPE_INITRD, 8,
+                           initrd_filename, strlen(initrd_filename) + 1);
+
 	stl_p(header+0x218, initrd_addr);
 	stl_p(header+0x21c, initrd_size);
     }
+    tpm_measure_end(fw_cfg);
 
     /* load kernel and setup */
     setup_size = header[0x1f1];
Index: qemu-git/tpm.h
===================================================================
--- qemu-git.orig/tpm.h
+++ qemu-git/tpm.h
@@ -1,6 +1,9 @@ 
 #ifndef _HW_TPM_CONFIG_H
 #define _HW_TPM_CONFIG_H
 
+#include "sysemu.h"
+#include "hw/fw_cfg.h"
+
 struct TPMState;
 typedef struct TPMState TPMState;
 
@@ -108,6 +111,36 @@  void do_info_tpm(Monitor *mon);
 void tpm_display_backend_drivers(FILE *out);
 const TPMDriverOps *tpm_get_backend_driver(const char *id);
 
+typedef enum TPMMeasureType {
+    TPM_MSR_TYPE_KERNEL_CMDLINE = 0x1105,
+    TPM_MSR_TYPE_KERNEL = 0x1205,
+    TPM_MSR_TYPE_INITRD = 0x1305,
+    TPM_MSR_TYPE_MODULE_CMDLINE = 0x1405,
+    TPM_MSR_TYPE_MODULE = 0x1505,
+} TPMMeasureType;
+
+typedef struct TPMMsrHdr {
+    uint16_t rev;
+    uint32_t totlen;
+    uint16_t numTPMMsrEntries;
+} __attribute__((packed)) TPMMsrHdr;
+
+typedef struct TPMMsrEntry {
+    uint32_t len;
+    uint32_t pcrindex;
+    uint32_t type;
+    uint8_t  digest[20];
+    uint32_t eventdatasize;
+    uint32_t event;
+} __attribute__((packed)) TPMMsrEntry;
+
+void tpm_measure_start(void);
+void tpm_measure_end(FWCfgState *s);
+void tpm_measure_file(const char *, TPMMeasureType type, uint8_t pcrindex);
+void tpm_measure_buffer(const void *buffer, long length,
+                        TPMMeasureType type, uint8_t pcrindex,
+                        const void *data, uint32_t data_len);
+
 extern TPMDriverOps tpm_builtin;
 
 #endif /* _HW_TPM_CONFIG_H */
Index: qemu-git/tpm.c
===================================================================
--- qemu-git.orig/tpm.c
+++ qemu-git/tpm.c
@@ -14,6 +14,9 @@ 
 #include "tpm.h"
 #include "monitor.h"
 #include "qerror.h"
+#include "sha1.h"
+#include "hw/loader.h"
+#include "bswap.h"
 
 
 #ifdef CONFIG_TPM
@@ -268,6 +271,88 @@  void tpm_config_parse(QemuOptsList *opts
     }
 }
 
+static TPMMsrHdr *tpm_measurements;
+
+void tpm_measure_start(void)
+{
+    if (!tpm_measurements) {
+        tpm_measurements = qemu_mallocz(sizeof(TPMMsrHdr));
+        tpm_measurements->rev = 1;
+        tpm_measurements->totlen = sizeof(TPMMsrHdr);
+    }
+}
+
+void tpm_measure_end(FWCfgState *s)
+{
+    uint32_t totlen = tpm_measurements->totlen;
+    /* fix endianess */
+    tpm_measurements->rev    = cpu_to_le16(tpm_measurements->rev);
+    tpm_measurements->totlen = cpu_to_le32(totlen);
+    tpm_measurements->numTPMMsrEntries =
+                               cpu_to_le16(tpm_measurements->numTPMMsrEntries);
+
+    fw_cfg_add_i32(s, FW_CFG_TPM_MEASURE_SIZE, totlen);
+    fw_cfg_add_bytes(s, FW_CFG_TPM_MEASURE_DATA,
+                     (unsigned char *)tpm_measurements, totlen);
+}
+
+static void tpm_measure_add_hash(unsigned char digest[20], TPMMeasureType type,
+                                 uint8_t pcrindex,
+                                 const void *data, uint32_t len)
+{
+    TPMMsrEntry *entry;
+
+    if (tpm_measurements) {
+        uint32_t entry_len = sizeof(TPMMsrEntry) + len;
+        tpm_measurements = qemu_realloc(tpm_measurements,
+                                        tpm_measurements->totlen +
+                                        entry_len);
+        if (tpm_measurements) {
+            entry = (void *)tpm_measurements + tpm_measurements->totlen;
+
+            tpm_measurements->totlen += entry_len;
+            tpm_measurements->numTPMMsrEntries++;
+
+            entry->len           = cpu_to_le32(entry_len);
+            entry->pcrindex      = cpu_to_le32(pcrindex);
+            entry->type          = cpu_to_le32(type);
+            memcpy(entry->digest, digest, sizeof(entry->digest));
+            entry->eventdatasize = cpu_to_le32(len);
+            if (len) {
+                memcpy(&entry->event, data, len);
+            }
+        }
+    }
+}
+
+void tpm_measure_buffer(const void *buffer, long len,
+                        TPMMeasureType type, uint8_t pcrindex,
+                        const void *data, uint32_t data_len)
+{
+    unsigned char hash[20];
+
+    qemu_sha1(hash, buffer, len);
+
+    tpm_measure_add_hash(hash, type, pcrindex, data, data_len);
+}
+
+void tpm_measure_file(const char *filename, TPMMeasureType type,
+                      uint8_t pcrindex)
+{
+    int len = get_image_size(filename);
+
+    if (len > 0) {
+        uint8_t *buffer = qemu_malloc(len);
+        if (buffer) {
+            if (load_image(filename, buffer) == len) {
+                tpm_measure_buffer(buffer, len, type, pcrindex,
+                                   filename, strlen(filename) + 1);
+            }
+            qemu_free(buffer);
+        }
+    }
+}
+
 # else /* TARGET_I386 || TARGET_X86_64 */
 
 void tpm_config_parse(QemuOptsList *opts_list, const char *optarg)
@@ -288,5 +373,28 @@  void do_info_tpm(Monitor *mon)
     monitor_printf(mon, "TPM support: not compiled\n");
 }
 
+
 # endif
+
+#else /* ! CONFIG_TPM */
+
+void tpm_measure_start(void)
+{
+}
+
+void tpm_measure_end(FWCfgState *s)
+{
+}
+
+void tpm_measure_buffer(const void *buffer, long len,
+                        TPMMeasureType type, uint8_t pcrindex,
+                        const void *data, uint32_t data_len)
+{
+}
+
+void tpm_measure_file(const char *filename, TPMMeasureType type,
+                      uint8_t pcrindex)
+{
+}
+
 #endif /* CONFIG_TPM */
Index: qemu-git/hw/fw_cfg.h
===================================================================
--- qemu-git.orig/hw/fw_cfg.h
+++ qemu-git/hw/fw_cfg.h
@@ -27,6 +27,8 @@ 
 #define FW_CFG_SETUP_SIZE       0x17
 #define FW_CFG_SETUP_DATA       0x18
 #define FW_CFG_FILE_DIR         0x19
+#define FW_CFG_TPM_MEASURE_SIZE 0x1a
+#define FW_CFG_TPM_MEASURE_DATA 0x1b
 
 #define FW_CFG_FILE_FIRST       0x20
 #define FW_CFG_FILE_SLOTS       0x10
Index: qemu-git/Makefile.target
===================================================================
--- qemu-git.orig/Makefile.target
+++ qemu-git/Makefile.target
@@ -233,7 +233,7 @@  obj-i386-y += debugcon.o multiboot.o
 obj-i386-y += pc_piix.o
 obj-i386-$(CONFIG_KVM) += kvmclock.o
 obj-i386-$(CONFIG_SPICE) += qxl.o qxl-logger.o qxl-render.o
-obj-i386-$(CONFIG_TPM) += tpm_tis.o
+obj-i386-$(CONFIG_TPM) += tpm_tis.o sha1.o
 obj-i386-$(CONFIG_TPM_BUILTIN) += tpm_builtin.o
 
 ifdef CONFIG_TPM_BUILTIN
Index: qemu-git/sha1.c
===================================================================
--- /dev/null
+++ qemu-git/sha1.c
@@ -0,0 +1,19 @@ 
+/*
+ * SHA1 Freebl wrapper
+ *
+ * Copyright (C) 2011 IBM Corporation
+ * Copyright (C) 2011 Stefan Berger
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2.  See
+ * the COPYING file in the top-level directory.
+ *
+ */
+
+#include "sha1.h"
+
+#include <nss3/blapi.h>
+
+int qemu_sha1(unsigned char hash[20], const unsigned char *data, uint32_t len)
+{
+    return SHA1_HashBuf(hash, data, len);
+}
Index: qemu-git/sha1.h
===================================================================
--- /dev/null
+++ qemu-git/sha1.h
@@ -0,0 +1,9 @@ 
+#ifndef __SHA1_H
+#define __SHA1_H
+
+#include <stdint.h>
+
+int qemu_sha1(unsigned char hash[20], const unsigned char *data,
+              uint32_t length);
+
+#endif /* __SHA1_H */
Index: qemu-git/configure
===================================================================
--- qemu-git.orig/configure
+++ qemu-git/configure
@@ -2578,6 +2578,29 @@  fi
 # libtpms probe
 
 if test "$tpm" = "yes" ; then
+  if $pkg_config --atleast-version=3.12.8 nss-softokn >/dev/null 2>&1 ; then
+    tpmsupport_cflags=$($pkg_config --cflags nss-softokn 2>/dev/null)
+    tpmsupport_libs="-lfreebl -lnspr4 -lnssutil3"
+    QEMU_CFLAGS="$QEMU_CFLAGS $tpmsupport_cflags"
+    LIBS="$LIBS $tpmsupport_libs"
+  else
+    feature_not_found "nss-softokn"
+  fi
+
+  # Check for nss-softokn-freebl-devel
+  cat > $TMPC <<EOF
+#include <blapi.h>
+int main(void) {
+  unsigned char hash[20];
+  char src[1];
+  return (int)SHA1_Hash(hash, src);
+}
+EOF
+
+  if ! compile_prog "" "$tpmsupport_libs" ; then
+    feature_not_found "nss-softokn-freebl-devel"
+  fi
+
   cat > $TMPC <<EOF
 #include <libtpms/tpm_library.h>
 int main(void) { return (int)TPMLIB_GetVersion(); }
Index: qemu-git/hw/multiboot.c
===================================================================
--- qemu-git.orig/hw/multiboot.c
+++ qemu-git/hw/multiboot.c
@@ -28,6 +28,7 @@ 
 #include "loader.h"
 #include "elf.h"
 #include "sysemu.h"
+#include "tpm.h"
 
 /* Show multiboot debug output */
 //#define DEBUG_MULTIBOOT
@@ -100,6 +101,8 @@  static uint32_t mb_add_cmdline(Multiboot
     target_phys_addr_t p = s->offset_cmdlines;
     char *b = (char *)s->mb_buf + p;
 
+    tpm_measure_buffer(cmdline, strlen(cmdline),
+                       TPM_MSR_TYPE_MODULE_CMDLINE, 8, NULL, 0);
     get_opt_value(b, strlen(cmdline) + 1, cmdline);
     s->offset_cmdlines += strlen(b) + 1;
     return s->mb_buf_phys + p;
@@ -283,6 +286,7 @@  int load_multiboot(void *fw_cfg,
             mbs.mb_buf_size = TARGET_PAGE_ALIGN(mb_mod_length + mbs.mb_buf_size);
             mbs.mb_buf = qemu_realloc(mbs.mb_buf, mbs.mb_buf_size);
 
+            tpm_measure_file(initrd_filename, TPM_MSR_TYPE_MODULE, 8);
             load_image(initrd_filename, (unsigned char *)mbs.mb_buf + offs);
             mb_add_mod(&mbs, mbs.mb_buf_phys + offs,
                        mbs.mb_buf_phys + offs + mb_mod_length, c);
@@ -299,6 +303,8 @@  int load_multiboot(void *fw_cfg,
     snprintf(kcmdline, sizeof(kcmdline), "%s %s",
              kernel_filename, kernel_cmdline);
     stl_p(bootinfo + MBI_CMDLINE, mb_add_cmdline(&mbs, kcmdline));
+    tpm_measure_buffer(kcmdline, strlen(kcmdline),
+                       TPM_MSR_TYPE_KERNEL_CMDLINE, 8, NULL, 0);
 
     stl_p(bootinfo + MBI_MODS_ADDR,  mbs.mb_buf_phys + mbs.offset_mbinfo);
     stl_p(bootinfo + MBI_MODS_COUNT, mbs.mb_mods_count); /* mods_count */
@@ -339,5 +345,7 @@  int load_multiboot(void *fw_cfg,
     option_rom[nb_option_roms].bootindex = 0;
     nb_option_roms++;
 
+    tpm_measure_end(fw_cfg);
+
     return 1; /* yes, we are multiboot */
 }