]> xenbits.xensource.com Git - people/liuw/stubdom.git/commitdiff
stubdom/grub: verify vTPM label if requested
authorDaniel De Graaf <dgdegra@tycho.nsa.gov>
Mon, 21 Apr 2014 17:23:04 +0000 (13:23 -0400)
committerIan Campbell <ian.campbell@citrix.com>
Wed, 23 Apr 2014 10:58:15 +0000 (11:58 +0100)
This adds an optional argument --vtpm-label=<label> to the pv-grub
command line.  If specified, a vtpm device must be connected to the
pv-grub domain and the backend of this device must have the given XSM
label (which may start with a * to indicate a wildcard).  Verifying the
label of the vTPM before sending measurements prevents a disaggregated
control domain that has access to xenstore but not to the guest domains
from causing the measurements performed by pv-grub to be discarded,
allowing the forgery of arbitrary kernel measurements in the TPM.

Signed-off-by: Daniel De Graaf <dgdegra@tycho.nsa.gov>
Cc: Stefano Stabellini <stefano.stabellini@eu.citrix.com>
Acked-by: Samuel Thibault <samuel.thibault@ens-lyon.org>
Acked-by: Ian Campbell <ian.campbell@citrix.com>
grub/kexec.c
grub/mini-os.c
grub/mini-os.h

index cef357eff99b2c70cd8ca1ce5b40fa38baf0a9e8..dc8db81044b6582e938b0ad30f0005492bf756b0 100644 (file)
@@ -68,6 +68,14 @@ struct pcr_extend_cmd {
        unsigned char hash[20];
 } __attribute__((packed));
 
+struct pcr_extend_rsp {
+       uint16_t tag;
+       uint32_t size;
+       uint32_t status;
+
+       unsigned char hash[20];
+} __attribute__((packed));
+
 /* Not imported from polarssl's header since the prototype unhelpfully defines
  * the input as unsigned char, which causes pointer type mismatches */
 void sha1(const void *input, size_t ilen, unsigned char output[20]);
@@ -135,20 +143,49 @@ int kexec_allocate(struct xc_dom_image *dom, xen_vaddr_t up_to)
     return 0;
 }
 
+/* Filled from mini-os command line or left as NULL */
+char *vtpm_label;
+
 static void tpm_hash2pcr(struct xc_dom_image *dom, char *cmdline)
 {
        struct tpmfront_dev* tpm = init_tpmfront(NULL);
-       uint8_t *resp;
+       struct pcr_extend_rsp *resp;
        size_t resplen = 0;
        struct pcr_extend_cmd cmd;
+       int rv;
 
-       /* If all guests have access to a vTPM, it may be useful to replace this
-        * with ASSERT(tpm) to prevent configuration errors from allowing a guest
-        * to boot without a TPM (or with a TPM that has not been sent any
-        * measurements, which could allow forging the measurements).
+       /*
+        * If vtpm_label was specified on the command line, require a vTPM to be
+        * attached and for the domain providing the vTPM to have the given
+        * label.
         */
-       if (!tpm)
+       if (vtpm_label) {
+               char ctx[128];
+               if (!tpm) {
+                       printf("No TPM found and vtpm_label specified, aborting!\n");
+                       do_exit();
+               }
+               rv = evtchn_get_peercontext(tpm->evtchn, ctx, sizeof(ctx) - 1);
+               if (rv < 0) {
+                       printf("Could not verify vtpm_label: %d\n", rv);
+                       do_exit();
+               }
+               ctx[127] = 0;
+               rv = strcmp(ctx, vtpm_label);
+               if (rv && vtpm_label[0] == '*') {
+                       int match_len = strlen(vtpm_label) - 1;
+                       int offset = strlen(ctx) - match_len;
+                       if (offset > 0)
+                               rv = strcmp(ctx + offset, vtpm_label + 1);
+               }
+
+               if (rv) {
+                       printf("Mismatched vtpm_label: '%s' != '%s'\n", ctx, vtpm_label);
+                       do_exit();
+               }
+       } else if (!tpm) {
                return;
+       }
 
        cmd.tag = bswap_16(TPM_TAG_RQU_COMMAND);
        cmd.size = bswap_32(sizeof(cmd));
@@ -156,15 +193,18 @@ static void tpm_hash2pcr(struct xc_dom_image *dom, char *cmdline)
        cmd.pcr = bswap_32(4); // PCR #4 for kernel
        sha1(dom->kernel_blob, dom->kernel_size, cmd.hash);
 
-       tpmfront_cmd(tpm, (void*)&cmd, sizeof(cmd), &resp, &resplen);
+       rv = tpmfront_cmd(tpm, (void*)&cmd, sizeof(cmd), (void*)&resp, &resplen);
+       ASSERT(rv == 0 && resp->status == 0);
 
        cmd.pcr = bswap_32(5); // PCR #5 for cmdline
        sha1(cmdline, strlen(cmdline), cmd.hash);
-       tpmfront_cmd(tpm, (void*)&cmd, sizeof(cmd), &resp, &resplen);
+       rv = tpmfront_cmd(tpm, (void*)&cmd, sizeof(cmd), (void*)&resp, &resplen);
+       ASSERT(rv == 0 && resp->status == 0);
 
        cmd.pcr = bswap_32(5); // PCR #5 for initrd
        sha1(dom->ramdisk_blob, dom->ramdisk_size, cmd.hash);
-       tpmfront_cmd(tpm, (void*)&cmd, sizeof(cmd), &resp, &resplen);
+       rv = tpmfront_cmd(tpm, (void*)&cmd, sizeof(cmd), (void*)&resp, &resplen);
+       ASSERT(rv == 0 && resp->status == 0);
 
        shutdown_tpmfront(tpm);
 }
index 9d4bcc76d5f3e9907889948ee0dcea32e19c185b..4fc052a25587fa1345f68f7b6762fb684449e8ae 100644 (file)
@@ -735,8 +735,14 @@ void __attribute__ ((noreturn)) grub_reboot (void)
  * for grub's 32bit pointers to work */
 char grub_scratch_mem[SCRATCH_MEMSIZE] __attribute__((aligned(PAGE_SIZE)));
 
-int main(int argc, char *argv[])
+int main(int argc, char **argv)
 {
+    if (argc > 1 && memcmp(argv[1], "--vtpm-label=", 13) == 0) {
+        vtpm_label = argv[1] + 13;
+        argc--;
+        argv++;
+    }
+
     if (argc > 1) {
         strncpy(config_file, argv[1], sizeof(config_file) - 1);
         config_file[sizeof(config_file) - 1] = 0;
index 6c6844122ee5e041e8eda3d7824bd6e16a4b9b59..9ec2bda0143d4f2e3307c53b094221f6a7b54d2f 100644 (file)
@@ -3,3 +3,5 @@ extern struct blkfront_dev **blk_dev;
 extern struct netfront_dev *net_dev;
 extern struct kbdfront_dev *kbd_dev;
 extern struct fbfront_dev *fb_dev;
+
+extern char* vtpm_label;