qemu-devel.nongnu.org archive mirror
 help / color / mirror / Atom feed
From: Stefan Berger <stefanb@linux.vnet.ibm.com>
To: stefanb@linux.vnet.ibm.com, qemu-devel@nongnu.org
Cc: anbang.ruan@cs.ox.ac.uk, andreas.niederl@iaik.tugraz.at,
	serge@hallyn.com
Subject: [Qemu-devel] [PATCH V6 12/13] Support for taking measurements when kernel etc. are passed to Qemu
Date: Wed, 06 Jul 2011 12:34:52 -0400	[thread overview]
Message-ID: <20110706163517.859122121@linux.vnet.ibm.com> (raw)
In-Reply-To: 20110706163440.987096936@linux.vnet.ibm.com

[-- Attachment #1: qemu_tpm_paravirt.diff --]
[-- Type: text/plain, Size: 11014 bytes --]

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.

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

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

Index: qemu-git/hw/pc.c
===================================================================
--- qemu-git.orig/hw/pc.c
+++ qemu-git/hw/pc.c
@@ -677,6 +677,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));
@@ -734,6 +737,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 {
@@ -795,9 +801,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,34 @@ 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,
+} 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
@@ -267,6 +270,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)
@@ -287,5 +372,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
@@ -241,7 +241,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
@@ -2471,6 +2471,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(); }

  parent reply	other threads:[~2011-07-06 16:36 UTC|newest]

Thread overview: 14+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2011-07-06 16:34 [Qemu-devel] [PATCH V6 00/13] Qemu Trusted Platform Module (TPM) integration Stefan Berger
2011-07-06 16:34 ` [Qemu-devel] [PATCH V6 01/13] Support for TPM command line options Stefan Berger
2011-07-06 16:34 ` [Qemu-devel] [PATCH V6 02/13] Add TPM (frontend) hardware interface (TPM TIS) to Qemu Stefan Berger
2011-07-06 16:34 ` [Qemu-devel] [PATCH V6 03/13] Add persistent state handling to TPM TIS frontend driver Stefan Berger
2011-07-06 16:34 ` [Qemu-devel] [PATCH V6 04/13] Add tpm_tis driver to build process Stefan Berger
2011-07-06 16:34 ` [Qemu-devel] [PATCH V6 05/13] Add a debug register Stefan Berger
2011-07-06 16:34 ` [Qemu-devel] [PATCH V6 06/13] Add a TPM backend skeleton implementation Stefan Berger
2011-07-06 16:34 ` [Qemu-devel] [PATCH V6 07/13] Implementation of the libtpms-based backend Stefan Berger
2011-07-06 16:34 ` [Qemu-devel] [PATCH V6 08/13] Introduce file lock for the block layer Stefan Berger
2011-07-06 16:34 ` [Qemu-devel] [PATCH V6 09/13] Add block storage support for libtpms based TPM backend Stefan Berger
2011-07-06 16:34 ` [Qemu-devel] [PATCH V6 10/13] Encrypt state blobs using AES CBC encryption Stefan Berger
2011-07-06 16:34 ` [Qemu-devel] [PATCH V6 11/13] Experimental support for block migrating TPMs state Stefan Berger
2011-07-06 16:34 ` Stefan Berger [this message]
2011-07-06 16:34 ` [Qemu-devel] [PATCH V6 13/13] Add a TPM backend null driver implementation Stefan Berger

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20110706163517.859122121@linux.vnet.ibm.com \
    --to=stefanb@linux.vnet.ibm.com \
    --cc=anbang.ruan@cs.ox.ac.uk \
    --cc=andreas.niederl@iaik.tugraz.at \
    --cc=qemu-devel@nongnu.org \
    --cc=serge@hallyn.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).