[ipxe-devel] [PATCH] Add BIOS TPM measurement support

Matthew Garrett matthew.garrett at nebula.com
Thu Aug 8 21:09:51 UTC 2013


Various security scenarios can benefit from the ability for systems to
either bind secrets to hardware in such a way that the secrets will only be
released if a known-good set of software was booted, or alternatively to
attest to a remote site that they're running a known-good set of software
in order to be granted access to a resource. This requires that the
bootloader be able to measure its configuration and the files it loads, and
then add these values to registers in a TPM.

This patch adds support for performing those measurements and extending the
PCR values. The iPXE configuration script will be measured into PCR 5, after
which the user may measure any files into any other PCR as they see fit. For
example:

kernel http://192.168.0.1/vmlinuz
tpm vmlinuz 8

will measure the downloaded vmlinuz file into PCR 8.

Signed-off-by: Matthew Garrett <matthew.garrett at nebula.com>
---
 src/config/config.c        |   3 +
 src/config/general.h       |   1 +
 src/hci/commands/tpm_cmd.c | 202 +++++++++++++++++++++++++++++++++++++++++++++
 src/image/script.c         |  12 +++
 src/include/ipxe/errfile.h |   1 +
 src/include/ipxe/tpm.h     |  12 +++
 6 files changed, 231 insertions(+)
 create mode 100644 src/hci/commands/tpm_cmd.c
 create mode 100644 src/include/ipxe/tpm.h

diff --git a/src/config/config.c b/src/config/config.c
index 06c5713..d0e31c6 100644
--- a/src/config/config.c
+++ b/src/config/config.c
@@ -263,6 +263,9 @@ REQUIRE_OBJECT ( nslookup_cmd );
 #ifdef PCI_CMD
 REQUIRE_OBJECT ( pci_cmd );
 #endif
+#ifdef TPM_CMD
+REQUIRE_OBJECT ( tpm_cmd );
+#endif
 
 /*
  * Drag in miscellaneous objects
diff --git a/src/config/general.h b/src/config/general.h
index 645f134..2c03df5 100644
--- a/src/config/general.h
+++ b/src/config/general.h
@@ -130,6 +130,7 @@ FILE_LICENCE ( GPL2_OR_LATER );
 //#define POWEROFF_CMD		/* Power off command */
 //#define IMAGE_TRUST_CMD	/* Image trust management commands */
 //#define PCI_CMD		/* PCI commands */
+//#define TPM_CMD		/* Image measurement commands */
 
 /*
  * ROM-specific options
diff --git a/src/hci/commands/tpm_cmd.c b/src/hci/commands/tpm_cmd.c
new file mode 100644
index 0000000..2786804
--- /dev/null
+++ b/src/hci/commands/tpm_cmd.c
@@ -0,0 +1,202 @@
+/*
+ * Copyright (C) 2013 Matthew Garrett <matthew.garrett at nebula.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER );
+
+#include <errno.h>
+#include <realmode.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <getopt.h>
+#include <ipxe/command.h>
+#include <ipxe/parseopt.h>
+#include <ipxe/image.h>
+#include <ipxe/crypto.h>
+#include <ipxe/sha1.h>
+#include <usr/imgmgmt.h>
+
+#undef ERRFILE
+#define ERRFILE ERRFILE_tpm
+
+/** @file
+ *
+ * TPM commands
+ *
+ */
+
+/** "tpm" options */
+struct tpm_options {};
+
+/** "tpm" option list */
+static struct option_descriptor tpm_opts[] = {};
+
+/** "tpm" command descriptor */
+static struct command_descriptor tpm_cmd =
+	COMMAND_DESC ( struct tpm_options, tpm_opts, 2, 2,
+		       "<image> <pcr>" );
+
+static uint8_t __data16_array ( tcg_buffer, [0x2a] );
+#define tcg_buffer __use_data16 ( tcg_buffer )
+
+int tpm_present ( void ) {
+	uint32_t tcg_ret = 0;
+
+	__asm__ __volatile__ ( REAL_CODE ( "int $0x1a\n\t" )
+			       : "=a" ( tcg_ret )
+			       : "a" ( 0xbb00 ),
+				 "b" ( 0x41504354 )
+			       : "%esi", "%edi", "%ecx");
+	if (tcg_ret == 0)
+		return 1;
+
+	return 0;
+}
+
+int update_pcr ( unsigned int pcr, uint8_t *digest ) {
+	unsigned int i;
+	uint16_t tcg_ret = 0;
+
+	// Input Parameter Block
+
+	tcg_buffer[0x0] = 0x2a;
+	tcg_buffer[0x1] = 0x0; // 0x002a ibl +8
+	tcg_buffer[0x2] = 0x0;
+	tcg_buffer[0x3] = 0x0; // 0x0000
+	tcg_buffer[0x4] = 0x22;
+	tcg_buffer[0x5] = 0x0; // 0x0022 obl +4
+	tcg_buffer[0x6] = 0x0;
+	tcg_buffer[0x7] = 0x0; // 0x0000
+
+	// TCG Command
+
+	tcg_buffer[0x8] = 0x0;
+	tcg_buffer[0x9] = 0xc1; // 0x00c1 tag
+	tcg_buffer[0xA] = 0x0;
+	tcg_buffer[0xB] = 0x0;
+	tcg_buffer[0xC] = 0x0;
+	tcg_buffer[0xD] = 0x22; // 0x00000022 length
+	tcg_buffer[0xE] = 0x0;
+	tcg_buffer[0xF] = 0x0;
+	tcg_buffer[0x10] = 0x0;
+	tcg_buffer[0x11] = 0x14; // 0x00000014 command ordinal
+	tcg_buffer[0x12] = 0;
+	tcg_buffer[0x13] = 0;
+	tcg_buffer[0x14] = 0;
+	tcg_buffer[0x15] = pcr;
+
+	for ( i = 0; i < sha1_algorithm.digestsize; i++ ) {
+		tcg_buffer[0x16+i] = digest[i];
+	}
+
+	__asm__ __volatile__ ( REAL_CODE ( "int $0x1a\n\t" )
+			       : "=a" ( tcg_ret )
+			       : "a" ( 0xbb02 ),
+				 "b" ( 0x41504354 ),
+				 "c" ( 0 ),
+				 "d" ( 0 ),
+				 "D" ( ( __from_data16( tcg_buffer ) ) ),
+				 "S" ( ( __from_data16( tcg_buffer ) ) ) );
+
+	if (tcg_ret) {
+		DBG ( "Received error code from TPM: %x\n", tcg_ret );
+		return -EIO;
+	}
+
+	return 0;
+}
+
+/**
+ * Generate a sha1 hash an image
+ *
+ * @v image		Image to hash
+ * @v digest_out	Output buffer. Must be at least 20 bytes long.
+ */
+void hash_image ( struct image *image, uint8_t *digest_out ) {
+	struct digest_algorithm *digest = &sha1_algorithm;
+	uint8_t digest_ctx[digest->ctxsize];
+	uint8_t buf[128];
+	size_t offset;
+	size_t len;
+	size_t frag_len;
+
+	offset = 0;
+	len = image->len;
+
+	/* calculate digest */
+	digest_init ( digest, digest_ctx );
+	while ( len ) {
+		frag_len = len;
+		if ( frag_len > sizeof ( buf ) )
+			frag_len = sizeof ( buf );
+		copy_from_user ( buf, image->data, offset, frag_len );
+		digest_update ( digest, digest_ctx, buf, frag_len );
+		len -= frag_len;
+		offset += frag_len;
+	}
+	digest_final ( digest, digest_ctx, digest_out );
+}
+/**
+ * The "tpm" command
+ *
+ * @v argc		Argument count
+ * @v argv		Argument list
+ * @ret rc		Return status code
+ */
+static int tpm_exec ( int argc, char **argv) {
+	struct tpm_options opts;
+	struct image *image;
+	int rc;
+	int pcr;
+	char *end;
+	uint8_t digest[sha1_algorithm.digestsize];
+
+	if ( ! tpm_present () )
+		return -ENODEV;
+
+	/* Parse options */
+	if ( ( rc = parse_options ( argc, argv, &tpm_cmd, &opts ) ) != 0 ) {
+		printf ( "Unable to parse options: %d\n", rc );
+		return rc;
+	}
+
+	/* Acquire image */
+	if ( ( rc = imgacquire ( argv[1], &image ) ) != 0 ) {
+		printf ( "Unable to acquire image: %d\n", rc );
+		return rc;
+	}
+
+	hash_image ( image, digest );
+
+	pcr = strtoul( argv[2], &end, 10 );
+
+	if ( *end || pcr < 8 || pcr > 15) {
+		printf ( "Invalid PCR \"%s\"\n", argv[2] );
+		return -EINVAL;
+	}
+
+	return update_pcr ( pcr, digest );
+}
+
+struct command tpm_command __command = {
+	.name = "tpm",
+	.exec = tpm_exec,
+};
+
diff --git a/src/image/script.c b/src/image/script.c
index 881d51f..25cc9e6 100644
--- a/src/image/script.c
+++ b/src/image/script.c
@@ -38,6 +38,9 @@ FILE_LICENCE ( GPL2_OR_LATER );
 #include <ipxe/shell.h>
 #include <usr/prompt.h>
 #include <ipxe/script.h>
+#include <ipxe/crypto.h>
+#include <ipxe/sha1.h>
+#include <ipxe/tpm.h>
 
 /** Offset within current script
  *
@@ -198,6 +201,15 @@ static int script_exec ( struct image *image ) {
 	 */
 	unregister_image ( image );
 
+#ifdef TPM_CMD
+	/* Measure the script */
+	if ( tpm_present () ) {
+		uint8_t digest[sha1_algorithm.digestsize];
+		hash_image ( image, digest );
+		update_pcr ( 5, digest );
+	}
+#endif
+
 	/* Preserve state of any currently-running script */
 	saved_offset = script_offset;
 
diff --git a/src/include/ipxe/errfile.h b/src/include/ipxe/errfile.h
index 0fd3fac..d135d10 100644
--- a/src/include/ipxe/errfile.h
+++ b/src/include/ipxe/errfile.h
@@ -285,6 +285,7 @@ FILE_LICENCE ( GPL2_OR_LATER );
 #define ERRFILE_linux_pci	      ( ERRFILE_OTHER | 0x003c0000 )
 #define ERRFILE_pci_settings	      ( ERRFILE_OTHER | 0x003d0000 )
 #define ERRFILE_efi_reboot	      ( ERRFILE_OTHER | 0x003e0000 )
+#define ERRFILE_tpm		      ( ERRFILE_OTHER | 0x003f0000 )
 
 /** @} */
 
diff --git a/src/include/ipxe/tpm.h b/src/include/ipxe/tpm.h
new file mode 100644
index 0000000..5b7d7f4
--- /dev/null
+++ b/src/include/ipxe/tpm.h
@@ -0,0 +1,12 @@
+#ifndef _IPXE_TPM_H
+#define _IPXE_TPM_H
+
+FILE_LICENCE ( GPL2_OR_LATER );
+
+#include <config/general.h>
+
+int tpm_present ( void );
+int update_pcr ( unsigned int pcr, uint8_t *digest );
+void hash_image (struct image *image, uint8_t *digest );
+
+#endif /* _IPXE_TPM_H */
-- 
1.8.3.1




More information about the ipxe-devel mailing list