[Top][All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: [Qemu-devel] [PATCH V8 14/14] Allow to provide inital TPM state
From: |
Michael S. Tsirkin |
Subject: |
Re: [Qemu-devel] [PATCH V8 14/14] Allow to provide inital TPM state |
Date: |
Thu, 1 Sep 2011 21:10:25 +0300 |
User-agent: |
Mutt/1.5.21 (2010-09-15) |
On Wed, Aug 31, 2011 at 10:36:05AM -0400, Stefan Berger wrote:
> This patch adds a -tpm ...,initstate=...,... command line option to the
> TPM's existing options and enables the TPM to be initialized with an
> existing state blob. This in turn allows us to simulate TPM manufacturing
> and equip the TPM with an endorsement key, certificates and initialize its
> NVRAM areas etc.. This step is typically done during manufacturng of the TPM
> and/or the (physical) machine.
>
> The initial state can be passed either as file or via a file descriptor. The
> encoding of the state can either be binary or in form of a base64-encoded
> blob surrounded by tags indicating the start and end.
>
> The intial state can be produced through a yet-to-be-published tpm-authoring
> tool.
>
> Signed-off-by: Stefan Berger <address@hidden>
I am guessing we get the base64 format from tpmlib?
> ---
> hw/tpm_builtin.c | 123
> ++++++++++++++++++++++++++++++++++++++++++++++++++++++-
> qemu-config.c | 12 +++++
> qemu-options.hx | 40 ++++++++++++++++-
> 3 files changed, 172 insertions(+), 3 deletions(-)
>
> Index: qemu-git/hw/tpm_builtin.c
> ===================================================================
> --- qemu-git.orig/hw/tpm_builtin.c
> +++ qemu-git/hw/tpm_builtin.c
> @@ -170,9 +170,16 @@ static struct enckey {
> AES_KEY tpm_dec_key;
> } enckey;
>
> +static int tpm_initstatefd = -1;
> +static bool tpm_initstate_bin;
> +
This hardcodes assumption of a single backend.
> static int tpm_builtin_load_sized_data_from_bs(BlockDriverState *bs,
> enum BSEntryType be,
> TPMSizedBuffer *tsb);
> +static TPM_RESULT tpm_builtin_get_initial_state(unsigned char **data,
> + uint32_t *length,
> + size_t tpm_number,
> + const char *name);
>
>
>
> @@ -1269,7 +1276,7 @@ static TPM_RESULT tpm_builtin_nvram_load
> *length = permanent_state.size;
>
> if (*length == 0) {
> - rc = TPM_RETRY;
> + rc = tpm_builtin_get_initial_state(data, length, tpm_number,
> name);
> } else {
> /* keep a copy of the last permanent state */
> rc = TPM_Malloc(data, *length);
> @@ -1452,6 +1459,94 @@ static TPM_RESULT tpm_builtin_io_getphys
> }
>
>
> +static TPM_RESULT tpm_builtin_get_initial_state(unsigned char **data,
> + uint32_t *length,
> + size_t tpm_number,
> + const char *name)
> +{
> + TPM_RESULT rc = TPM_RETRY;
> + uint32_t allocated = 0;
> + int len, flags;
> + unsigned char buf[1024];
> + unsigned char *result = NULL;
> + size_t result_len;
> +
> + if (tpm_initstatefd >= 0) {
> + *data = NULL;
> + *length = 0;
> +
> + flags = fcntl(tpm_initstatefd, F_GETFL);
> + if (flags < 0 ||
> + fcntl(tpm_initstatefd, F_SETFL, flags & ~O_NONBLOCK) < 0) {
> + return TPM_FAIL;
> + }
> +
> + while (TRUE) {
> + len = read(tpm_initstatefd, buf, sizeof(buf));
> +
> + if (len > 0) {
> + if (len > allocated - *length) {
> + allocated = *length + len + 1024;
> + if (TPM_Realloc(data, allocated) != TPM_SUCCESS) {
> + goto err_exit;
> + }
> + }
> + memcpy(&(*data)[*length], buf, len);
> + *length += len;
> + (*data)[*length] = 0;
> + } else if (len == 0) {
> + rc = TPM_SUCCESS;
> + break;
> + } else if (len < 0) {
> + if (errno == EINTR) {
> + continue;
> + }
> + goto err_exit;
> + }
> + }
> +
> + if (*data == NULL) {
> + /* nothing read */
> + rc = TPM_FAIL;
> + goto err_exit;
> + }
> +
> + if (!tpm_initstate_bin) {
> + if (TPMLIB_DecodeBlob((char *)*data, TPMLIB_BLOB_TYPE_INITSTATE,
> + &result, &result_len) != TPM_SUCCESS) {
> + goto err_exit;
> + }
> + TPM_Free(*data);
> + *data = result;
> + *length = result_len;
> + result = NULL;
> + }
> + /* sanity check for the size of the blob */
> + if (*length > tpmlib_get_prop(TPMPROP_TPM_MAX_NV_SPACE)) {
> + goto err_exit;
> + }
Do we really have to hand-craft file reading?
How large is TPMPROP_TPM_MAX_NV_SPACE?
If not too large, we can just allocate that
and do a single fread call?
Or, we rely on glib now - can we use
g_io_channel_read_to_end () or something like that?
> + /* have it written into the BlockStorage */
> + rc = tpm_builtin_nvram_storedata(*data, *length, tpm_number, name);
What if that backend is compiled-out?
link will fail?
> + if (rc != TPM_SUCCESS) {
> + goto err_exit;
> + }
> + }
> +
> +norm_exit:
> + close(tpm_initstatefd);
> + tpm_initstatefd = -1;
> +
> + return rc;
> +
> +err_exit:
> + TPM_Free(*data);
> + *data = NULL;
> + *length = 0;
> + TPM_Free(result);
> +
> + goto norm_exit;
> +}
> +
> /*****************************************************************/
>
>
> @@ -1748,6 +1843,7 @@ static TPMBackend *tpm_builtin_create(Qe
> const char *value;
> unsigned char keyvalue[256/8];
> int keysize = sizeof(keyvalue);
> + unsigned int offset;
>
> driver = g_malloc(sizeof(TPMBackend));
> if (!driver) {
> @@ -1801,6 +1897,28 @@ static TPMBackend *tpm_builtin_create(Qe
> enckey.enctype = BS_DIR_ENCTYPE_NONE;
> }
>
> + value = qemu_opt_get(opts, "initstate");
> + if (value) {
> + offset = 0;
> +
> + if (!strncmp(value, "bin:", 4)) {
> + tpm_initstate_bin = true;
> + offset = 4;
> + } else if (!strncmp(value, "base64:", 7)) {
> + tpm_initstate_bin = false;
> + offset = 7;
> + }
> +
> + if (sscanf(&value[offset], "fd:%d", &tpm_initstatefd) != 1) {
> + tpm_initstatefd = open(&value[offset], O_RDONLY);
> + if (tpm_initstatefd < 0) {
> + fprintf(stderr, "tpm: could not open file '%s' for
> reading.\n",
> + value);
> + goto err_exit;
> + }
> + }
> + }
> +
Separate options for fd and for file mode would be better.
> return driver;
>
> err_exit:
> @@ -1816,6 +1934,9 @@ static void tpm_builtin_destroy(TPMBacke
> g_free(driver->id);
> g_free(driver->model);
> g_free(driver);
> +
> + close(tpm_initstatefd);
> + tpm_initstatefd = -1;
> }
>
>
> Index: qemu-git/qemu-config.c
> ===================================================================
> --- qemu-git.orig/qemu-config.c
> +++ qemu-git/qemu-config.c
> @@ -527,6 +527,12 @@ static QemuOptsList qemu_tpmdev_opts = {
> .type = QEMU_OPT_STRING,
> .help = "Data encryption key",
> },
> + {
> + .name = "initstate",
> + .type = QEMU_OPT_STRING,
> + .help = "File or file descriptor for reading initial TPM state "
> + "from",
> + },
> { /* end of list */ }
> },
> };
> @@ -556,6 +562,12 @@ static QemuOptsList qemu_tpm_opts = {
> .type = QEMU_OPT_STRING,
> .help = "Data encryption key",
> },
> + {
> + .name = "initstate",
> + .type = QEMU_OPT_STRING,
> + .help = "File or file descriptor for reading initial TPM state "
> + "from",
> + },
> { /* end of list */ }
> },
> };
I think description should document the magic bin:/base64: etc strings,
or better get rid of them.
> Index: qemu-git/qemu-options.hx
> ===================================================================
> --- qemu-git.orig/qemu-options.hx
> +++ qemu-git/qemu-options.hx
> @@ -1767,8 +1767,10 @@ DEFHEADING(TPM device options:)
> DEF("tpm", HAS_ARG, QEMU_OPTION_tpm, \
> "" \
> "-tpm builtin,path=<path>[,model=<model>][,key=<aes key>]\n" \
> + " [,initstate=[bin:|base64:]fd:<fd>|<path>]\n" \
> " enable a builtin TPM with state in file in path\n" \
> " and encrypt the TPM's state with the given AES key\n" \
> + " initstate= path to initial state of TPM; default is
> base64\n" \
> "-tpm null enable a TPM null driver that responds with a fault\n" \
> " message to every TPM request\n" \
> "-tpm model=? to list available TPM device models\n" \
> @@ -1800,7 +1802,7 @@ Use ? to print all available TPM backend
> qemu -tpmdev ?
> @end example
>
> address@hidden -tpmdev builtin ,address@hidden, address@hidden
> [,address@hidden
> address@hidden -tpmdev builtin ,address@hidden, address@hidden
> [,address@hidden [,address@hidden
>
> Creates an instance of the built-in TPM.
>
> @@ -1830,6 +1832,40 @@ using AES-CBC encryption scheme supply t
> -tpmdev
> builtin,id=tpm0,path=<path_to_qcow2>,key=aes-cbc:0x1234567890abcdef01234567890abcdef
> -device tpm-tis,tpmdev=tpm0
> @end example
>
> address@hidden specifies the path to a file containing the initial
> +state of the TPM. It can be used to provide the TPM with an EK and
> certificates
> +for the EK, TPM and Platform. Since the file contains binary data that
> +have to conform to the TPM's layout of data, it must have been created using
> +an approriate authoring tool.
> +
> +The initstate option allows to provide a binary state blob or one that is
> +encode in base 64. The base64-encode state blob must have the format
> +
> address@hidden
> +-----BEGIN INITSTATE-----
> +<base 64 encoded state>
> +-----END INITSTATE-----
> address@hidden example
> +
> +The initstate option is only effective when Qemu is started with blank
> +state.
> +
> +The initstate option supports several formats:
> +
> address@hidden @option
> + @item [base64:]<path_to_blob>
> + Provide the path to the TPM's initial state blob in base64 format.
> + @item bin:<path to blob>
> + Provide the path to the TPM's initial state blob in binary format.
> + @item [base64:]fd:<fd>
> + Provide the base64 formatted initial state via a file descriptor to read
> from.
> + @item bin:fd:<fd>
> + Provide the binary initial state via a file descriptor to read from.
The command line is non standard. E.g. what if the path starts with fd?
> address@hidden table
> +
> address@hidden is optional.
> +
> +
> @item -tpmdev null
>
> Creates an instance of a TPM null driver that responds to every command
> @@ -1840,7 +1876,7 @@ with a fault message.
> The short form of a TPM device option is:
> @table @option
>
> address@hidden -tpm @var{backend-type}, address@hidden [,address@hidden
> [,address@hidden
> address@hidden -tpm @var{backend-type}, address@hidden [,address@hidden
> [,address@hidden [,address@hidden
> @findex -tpm
>
> @option{model} specifies the device model. The default device model is a
>
- Re: [Qemu-devel] [PATCH V8 14/14] Allow to provide inital TPM state,
Michael S. Tsirkin <=