[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [Xen-devel] [PATCH 2/2] vtpmmgr: execute deep quote in locality 0





On Mon, Apr 6, 2015 at 6:49 PM, Daniel De Graaf <dgdegra@xxxxxxxxxxxxx> wrote:
On 04/05/2015 07:09 AM, Emil Condrea wrote:
Enables deep quote execution for vtpmmgr which can not be started
using locality 2. The VTPM_ORD_GET_QUOTE command is backwards
compatible. When receives flags=0 from vTPM it extends additional
information into the PCRs as it did before.

Even without extending values into the resettable PCRs, the PCR
selection for the quote should not be ignored: otherwise, the
measurement of the hypervisor (stored in either PCR 4-5 or PCR 18-19)
cannot be included in a deep quote. Allowing a guest to attest to the
measurement of its hypervisor is one of the major reasons for allowing a
deep quote and is likely to be mandatory in a vTPM provisioning quote.
very good point, I missed that someone would include additional PCRs in the
request.

The backwards compatibility added here has some caveats that need to be
treated carefully. In the current version, it is not possible to
distinguish between a request that has the new externData structure and
a request that uses flags=0 where the vTPM itself has constructed a
requestData value that looks like a valid externData.

The simplest solution is to always use the structure prepared by the
vTPM manager for externData and remove backwards compatibility from the
series. Since the new quote format would be introduced with a new
version of the vTPM Manager and (likely) a new hypervisor, any support
software that makes use of the quotes can be updated at the same time.

If backwards compatibility is needed, then there must be a way to
distinguish a new-format quote from an old-format quote. If the reset
of the modified PCRs is moved to the end of the quote operation (instead
of the beginning, as is currently done), the value of PCR 20 will be
at its reset value (either 00..00 or ff..ff) when the new-format quote
is requested. Including the value of PCR 20 in all new-format quotes
would then be sufficient evidence that the externData value in the quote
was generated by the vTPM Manager.

In addition, it would be useful to add a command-line switch to the vTPM
manager that disables this backwards compatibility mode if a given
system will not be using it: this would avoid issues if the users of
that system have not read this discussion and decide to use quotes that
do not include PCR 20-23 (which is normally a sensible thing to do).

I think it is reasonable to specify in the documentation the changes and
include only the new version.Â
Flags are interpreted as a bitmask of:
 * VTPM_QUOTE_FLAGS_HASH_UUID
 * VTPM_QUOTE_FLAGS_VTPM_MEASUREMENTS
 * VTPM_QUOTE_FLAGS_GROUP_INFO
When using flags!=0, vtpmmgr ignores quoteSelect and TPM_Quote is
executed with:
 ÂexternData = SHA1(
  flags,
  requestData,
  [UUIDs if requested],
  [vTPM measurements if requested],
  [vTPM group update policy if requested]
 Â)

This specification does not match what you actually do below. There are
some advantages to doing it the way you have coded, but the method for
calculating the hash needs to be fully documented.

I will update the documentation: sha1(UUIDs) instead of UUIDs and so on.
Signed-off-by: Emil Condrea <emilcondrea@xxxxxxxxx>
---
[...]

diff --git a/stubdom/vtpmmgr/mgmt_authority.c b/stubdom/vtpmmgr/mgmt_authority.c
index 0526a12..0e4aac7 100644
--- a/stubdom/vtpmmgr/mgmt_authority.c
+++ b/stubdom/vtpmmgr/mgmt_authority.c
@@ -128,6 +128,49 @@ static int do_load_aik(struct mem_group *group, TPM_HANDLE *handle)
    return TPM_LoadKey(TPM_SRK_KEYHANDLE, &key, handle, (void*)&vtpm_globals.srk_auth, &vtpm_globals.oiap);
 }

+static void do_vtpminfo_hash(uint32_t extra_info_flags,struct mem_group *group,
+Â Â Â Âconst void* uuid, const uint8_t* kern_hash,unsigned char** calc_hashes)
+{
+Â Â Â Âint i;
+Â Â Â Âsha1_context ctx;
+Â Â Â Âif(extra_info_flags & VTPM_QUOTE_FLAGS_HASH_UUID){
+Â Â Â Â Â Â Â Âprintk("hashing for FLAGS_HASH_UUID: ");
+Â Â Â Â Â Â Â Âif(uuid){
+Â Â Â Â Â Â Â Â Â Â Â Âprintk("true");
+Â Â Â Â Â Â Â Â Â Â Â Âsha1_starts(&ctx);
+Â Â Â Â Â Â Â Â Â Â Â Âsha1_update(&ctx, (void*)uuid, 16);
+Â Â Â Â Â Â Â Â Â Â Â Âsha1_finish(&ctx, *calc_hashes);
+Â Â Â Â Â Â Â Â Â Â Â Â*calc_hashes = *calc_hashes + 20;
+Â Â Â Â Â Â Â Â}
+Â Â Â Â Â Â Â Âprintk("\n");
+Â Â Â Â}
+Â Â Â Âif(extra_info_flags & VTPM_QUOTE_FLAGS_VTPM_MEASUREMENTS){
+Â Â Â Â Â Â Â Âprintk("hashing for VTPM_QUOTE_FLAGS_VTPM_MEASUREMENTS: ");
+Â Â Â Â Â Â Â Âif(kern_hash){
+Â Â Â Â Â Â Â Â Â Â Â Âprintk("true");
+Â Â Â Â Â Â Â Â Â Â Â Âsha1_starts(&ctx);
+Â Â Â Â Â Â Â Â Â Â Â Âsha1_update(&ctx, (void*)kern_hash, 20);
+Â Â Â Â Â Â Â Â Â Â Â Âsha1_finish(&ctx, *calc_hashes);
+Â Â Â Â Â Â Â Â Â Â Â Â*calc_hashes = *calc_hashes + 20;
+Â Â Â Â Â Â Â Â}
+Â Â Â Â Â Â Â Âprintk("\n");
+Â Â Â Â}

If exactly one of (uuid) and (kern_hash) is NULL and both were requested
in extra_info_flags, it is not easy to determine which one was used. A
better method would be to only place the sha1_update calls inside the
NULL check and always calculating the SHA1. This makes interpreting the
result easier: for a given value of extra_info_flags, the size of the
externData structure is of constant size. The presence of the SHA1 of
the empty string is a sentinel value indicating that the information is
not available.
Indeed. Constant size for externData is a must.

+Â Â Â Âif(extra_info_flags & VTPM_QUOTE_FLAGS_GROUP_INFO){
+Â Â Â Â Â Â Â Âprintk("hashing for VTPM_QUOTE_FLAGS_GROUP_INFO: true");
+Â Â Â Â Â Â Â Âsha1_starts(&ctx);
+Â Â Â Â Â Â Â Âsha1_update(&ctx, (void*)&group->id_data.saa_pubkey, sizeof(group->id_data.saa_pubkey));
+Â Â Â Â Â Â Â Âsha1_update(&ctx, (void*)&group->details.cfg_seq, 8);
+Â Â Â Â Â Â Â Âsha1_update(&ctx, (void*)&group->seal_bits.nr_cfgs, 4);
+Â Â Â Â Â Â Â Âfor(i=0; i < group->nr_seals; i++)
+Â Â Â Â Â Â Â Â Â Â Â Âsha1_update(&ctx, (void*)&group->seals[i].digest_release, 20);
+Â Â Â Â Â Â Â Âsha1_update(&ctx, (void*)&group->seal_bits.nr_kerns, 4);
+Â Â Â Â Â Â Â Âsha1_update(&ctx, (void*)&group->seal_bits.kernels, 20 * be32_native(group->seal_bits.nr_kerns));
+Â Â Â Â Â Â Â Âsha1_finish(&ctx, *calc_hashes);
+Â Â Â Â Â Â Â Â*calc_hashes = *calc_hashes + 20;
+Â Â Â Â Â Â Â Âprintk("\n");
+Â Â Â Â}
+}

You should return an error if an unknown bit in extra_info_flags is set;
this will make it easier to extend the flags later.
will do.

One additional flag that would be useful (I can add it later if you don't
want to add it in the first version):

VTPM_QUOTE_FLAGS_GROUP_PUBKEY - value is SHA1(group->id_data.saa_pubkey).
    This value does not change when the configuration of a group is
updated, making it a less informative but more easily verified version of
VTPM_QUOTE_FLAGS_GROUP_INFO.
I will include it in the next patch series.


 /*
 Â* Sets up resettable PCRs for a vTPM deep quote request
 Â*/
@@ -273,18 +316,44 @@ int group_do_activate(struct mem_group *group, void* blob, int blobSize,

 int vtpm_do_quote(struct mem_group *group, const uuid_t uuid,
    const uint8_t* kern_hash, const struct tpm_authdata *data, TPM_PCR_SELECTION *sel,
-Â Â Â Âvoid* pcr_out, uint32_t *pcr_size, void* sig_out)
+Â Â Â Âuint32_t extra_info_flags, void* pcr_out, uint32_t *pcr_size, void* sig_out)
 {
    TPM_HANDLE handle;
    TPM_AUTH_SESSION oiap = TPM_AUTH_SESSION_INIT;
    TPM_PCR_COMPOSITE pcrs;
    BYTE* sig;
    UINT32 size;
-Â Â Â Âint rc;
+Â Â Â Âsha1_context ctx;
+Â Â Â ÂTPM_DIGEST externData;
+Â Â Â Âconst void* data_to_quote = data;
+Â Â Â ÂUINT32 pcrSelSize = sel->sizeOfSelect;
+Â Â Â Âunsigned char* ppcr_out = (unsigned char*)pcr_out;
+Â Â Â Âunsigned char** pcr_outv = (unsigned char**)&ppcr_out;

-Â Â Â Ârc = do_pcr_setup(group, uuid, kern_hash);
-Â Â Â Âif (rc)
-Â Â Â Â Â Â Â Âreturn rc;
+Â Â Â Âint rc;
+Â Â Â Âif(extra_info_flags == 0){
+Â Â Â Â Â Â Â Âprintk("Extra Info Flags == 0, hope you are on locality 2\n");
+Â Â Â Â Â Â Â Ârc = do_pcr_setup(group, uuid, kern_hash);
+Â Â Â Â Â Â Â Âif (rc)
+Â Â Â Â Â Â Â Â Â Â Â Âreturn rc;
+Â Â Â Â}
+Â Â Â Âelse{
+Â Â Â Â Â Â Â Âprintk("Extra Info Flags =0x%x\n",extra_info_flags);
+Â Â Â Â Â Â Â Â//resetting pcr selection
+Â Â Â Â Â Â Â Âif(sel->sizeOfSelect > sizeof(sel->pcrSelect))
+Â Â Â Â Â Â Â Â Â Â Â ÂpcrSelSize = sizeof(sel->pcrSelect);
+Â Â Â Â Â Â Â Âmemset(sel->pcrSelect,0,pcrSelSize);
+
+Â Â Â Â Â Â Â Âsha1_starts(&ctx);
+Â Â Â Â Â Â Â Âsha1_update(&ctx, (void*)&extra_info_flags, 4);
+Â Â Â Â Â Â Â Âsha1_update(&ctx, (void*)data, 20);
+Â Â Â Â Â Â Â Âdo_vtpminfo_hash(extra_info_flags,group, uuid, kern_hash, pcr_outv);
+Â Â Â Â Â Â Â Â*pcr_size = *pcr_outv - (unsigned char*)pcr_out;

The values of pcr_out and pcr_size will be NULL when called from GroupRegister.
I will use an additional pointer if pcr_out is NULL in order to include in externData
the hash for VTPM_QUOTE_FLAGS_GROUP_INFO if requested.

+Â Â Â Â Â Â Â Âif(*pcr_size > 0)
+Â Â Â Â Â Â Â Â Â Â Â Âsha1_update(&ctx, pcr_out, *pcr_size);
+Â Â Â Â Â Â Â Âsha1_finish(&ctx, externData.digest);
+Â Â Â Â Â Â Â Âdata_to_quote = (void*)externData.digest;
+Â Â Â Â}

    rc = do_load_aik(group, &handle);
    if (rc)
@@ -296,8 +365,7 @@ int vtpm_do_quote(struct mem_group *group, const uuid_t uuid,
        return rc;
    }

-Â Â Â Ârc = TPM_Quote(handle, (void*)data, sel, (void*)&group->aik_authdata, &oiap, &pcrs, &sig, &size);
-Â Â Â Âprintk("TPM_Quote: %d\n", rc);
+Â Â Â Ârc = TPM_Quote(handle, data_to_quote, sel, (void*)&group->aik_authdata, &oiap, &pcrs, &sig, &size);

    TPM_TerminateHandle(oiap.AuthHandle);
    TPM_FlushSpecific(handle, TPM_RT_KEY);
@@ -310,8 +378,11 @@ int vtpm_do_quote(struct mem_group *group, const uuid_t uuid,
    }

    if (pcr_out) {
-Â Â Â Â Â Â Â Â*pcr_size = pcrs.valueSize;
-Â Â Â Â Â Â Â Âmemcpy(pcr_out, pcrs.pcrValue, *pcr_size);
+Â Â Â Â Â Â Â Â/*hashes already copied when flags!=0 by do_vtpminfo_hash*/
+Â Â Â Â Â Â Â Âif(extra_info_flags == 0){
+Â Â Â Â Â Â Â Â Â Â Â Â*pcr_size = pcrs.valueSize;
+Â Â Â Â Â Â Â Â Â Â Â Âmemcpy(pcr_out, pcrs.pcrValue, *pcr_size);
+Â Â Â Â Â Â Â Â}
    }

I think it would be useful to append the PCR values to the externData values,
as long as the entire set of hashes doesn't risk becoming too long.
Right now the hashes used for externData are written in pcr_out. Should we limit
the pcr_out size to a certain value?
If there will be a limit for pcr_out, the domU executing the quote will be able to
read the PCRs for physical TPM using tag TPM_TAG_RQU_COMMAND and
ord TPM_ORD_PcrRead using passthrough, right?
If domU executing the deep quote requests 10 PCRs to be included in the quote
it will receive in pcr_out just the hashes used for externalData and the PCR values
should be obtained later.

Now, the maximum hashes number for calculating the externData is 3
( 4 after including VTPM_QUOTE_FLAGS_GROUP_PUBKEY). In the future if
there will be much more hashes we can implement something like
vtpmmgr_GetBootHash for domU clients, so the validation data can be obtained
with additional requests to vtpmmgr. Is it a good idea?

Also, I have an implementation for requesting a deep quote for trousers and
tpm tools. Do you think it is worth to send patches to those repositories also, since
TPM_ORD_DeepQuote is not included in the TPM 1.2 specification ?

Thanks for the review!

Emil Condrea
Â

--
Daniel De Graaf
National Security Agency

_______________________________________________
Xen-devel mailing list
Xen-devel@xxxxxxxxxxxxx
http://lists.xen.org/xen-devel

 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.