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

Re: [Xen-devel] [PATCH] stubdom/vtpm: make state save operation atomic



On 11/29/2012 01:07 PM, Matthew Fioravante wrote:
> On 11/27/2012 05:16 PM, Daniel De Graaf wrote:
>> This changes the save format of the vtpm stubdom to include two copies
>> of the saved data: one active, and one inactive. When saving the state,
>> data is written to the inactive slot before updating the key and hash
>> saved with the TPM Manager, which determines the active slot when the
>> vTPM starts up.
>>
>> Signed-off-by: Daniel De Graaf <dgdegra@xxxxxxxxxxxxx>
>> ---
>>   stubdom/vtpm/vtpmblk.c | 66 
>> +++++++++++++++++++++++++++++++++++++++++---------
>>   1 file changed, 54 insertions(+), 12 deletions(-)
>>
>> diff --git a/stubdom/vtpm/vtpmblk.c b/stubdom/vtpm/vtpmblk.c
>> index b343bd8..f988606 100644
>> --- a/stubdom/vtpm/vtpmblk.c
>> +++ b/stubdom/vtpm/vtpmblk.c
>> @@ -23,6 +23,8 @@
>>     /*Encryption key and block sizes */
>>   #define BLKSZ 16
>> +/* Maximum size of one saved-state slot */
>> +#define SLOT_SIZE 32768
> Instead of statically defining a slot size and stressing over whether or not 
> it may be too small, why not make it half the size of the disk image? You can 
> retrieve the disk size by doing fstat(blkfront_fd).

Ah, I didn't see that information was available in mini-os. Actually, the 
blkinfo
structure in init_vtpmblk is more convenient, so I'll use that.

>>     static struct blkfront_dev* blkdev = NULL;
>>   static int blkfront_fd = -1;
>> @@ -59,15 +61,20 @@ void shutdown_vtpmblk(void)
>>      blkdev = NULL;
>>   }
>>   -int write_vtpmblk_raw(uint8_t *data, size_t data_length)
>> +static int write_vtpmblk_raw(uint8_t *data, size_t data_length, int slot)
>>   {
>>      int rc;
>>      uint32_t lenbuf;
>>      debug("Begin Write data=%p len=%u", data, data_length);
> Should add which slot were writing to and possibly the disk offset as well to 
> the debug print

A number of debug outputs will be improved with slot info.

>>   +   if (data_length > SLOT_SIZE - 4) {
>> +      error("write(data) cannot fit in data slot (%d). Increase 
>> SLOT_SIZE.", data_length);
>> +      return -1;
>> +   }
>> +
>>      lenbuf = cpu_to_be32((uint32_t)data_length);
>>   -   lseek(blkfront_fd, 0, SEEK_SET);
>> +   lseek(blkfront_fd, slot * SLOT_SIZE, SEEK_SET);
>>      if((rc = write(blkfront_fd, (uint8_t*)&lenbuf, 4)) != 4) {
>>         error("write(length) failed! error was %s", strerror(errno));
>>         return -1;
>> @@ -82,12 +89,12 @@ int write_vtpmblk_raw(uint8_t *data, size_t data_length)
>>      return 0;
>>   }
>>   -int read_vtpmblk_raw(uint8_t **data, size_t *data_length)
>> +static int read_vtpmblk_raw(uint8_t **data, size_t *data_length, int slot)
>>   {
>>      int rc;
>>      uint32_t lenbuf;
>>   -   lseek(blkfront_fd, 0, SEEK_SET);
>> +   lseek(blkfront_fd, slot * SLOT_SIZE, SEEK_SET);
>>      if(( rc = read(blkfront_fd, (uint8_t*)&lenbuf, 4)) != 4) {
>>         error("read(length) failed! error was %s", strerror(errno));
>>         return -1;
>> @@ -97,6 +104,10 @@ int read_vtpmblk_raw(uint8_t **data, size_t *data_length)
>>         error("read 0 data_length for NVM");
>>         return -1;
>>      }
>> +   if(*data_length > SLOT_SIZE - 4) {
>> +      error("read invalid data_length for NVM");
>> +      return -1;
>> +   }
>>        *data = tpm_malloc(*data_length);
>>      if((rc = read(blkfront_fd, *data, *data_length)) != *data_length) {
>> @@ -221,6 +232,8 @@ egress:
>>      return rc;
>>   }
>>   +static int active_slot = -1;
> I think we should initialize to 0 or 1 because -1 is an invalid state.
> For a new vtpm, read_vtpmblk() will fail and I think this is what happens:
> read_vtpmblk()
> try_fetch_key_from_manager() -> fails
> goto abort_egress
> active_slot = -1 (you have this below)

Yes, that's intentional. That way, you can tell the difference between slot 1
being active and no slot being active.
 
> Then later
> write_vtpmblk()
> active_slot = !active_slot (!-1 = 0)
> 
> The last line happens to work correctly but I don't think its very clear. I 
> think we should initialize to 1 and set it to 1 if read_vtpmblk() fails.

I think I'll just put a comment at the !active_slot to note that active_slot is
-1 => 0 if no active slots existed.

> Also a minor nitpick, the name is active_slot, but what it really is is the 
> last_slot_written_to because everytime you write you change it first.

That still makes it the active slot: at the point when it's changed, the value 
is
the new active slot (and we're just working to commit that change to disk).

>> +
>>   int write_vtpmblk(struct tpmfront_dev* tpmfront_dev, uint8_t* data, size_t 
>> data_length) {
>>      int rc;
>>      uint8_t* cipher = NULL;
>> @@ -228,12 +241,15 @@ int write_vtpmblk(struct tpmfront_dev* tpmfront_dev, 
>> uint8_t* data, size_t data_
>>      uint8_t hashkey[HASHKEYSZ];
>>      uint8_t* symkey = hashkey + HASHSZ;
>>   +   /* Switch to the other slot */
>> +   active_slot = !active_slot;
>> +
>>      /* Encrypt the data */
>>      if((rc = encrypt_vtpmblk(data, data_length, &cipher, &cipher_len, 
>> symkey))) {
>>         goto abort_egress;
>>      }
>>      /* Write to disk */
>> -   if((rc = write_vtpmblk_raw(cipher, cipher_len))) {
>> +   if((rc = write_vtpmblk_raw(cipher, cipher_len, active_slot))) {
>>         goto abort_egress;
>>      }
>>      /* Get sha1 hash of data */
>> @@ -256,7 +272,8 @@ int read_vtpmblk(struct tpmfront_dev* tpmfront_dev, 
>> uint8_t** data, size_t *data
>>      size_t cipher_len = 0;
>>      size_t keysize;
>>      uint8_t* hashkey = NULL;
>> -   uint8_t hash[HASHSZ];
>> +   uint8_t hash0[HASHSZ];
>> +   uint8_t hash1[HASHSZ];
>>      uint8_t* symkey;
>>        /* Retreive the hash and the key from the manager */
>> @@ -270,14 +287,32 @@ int read_vtpmblk(struct tpmfront_dev* tpmfront_dev, 
>> uint8_t** data, size_t *data
>>      }
>>      symkey = hashkey + HASHSZ;
>>   -   /* Read from disk now */
>> -   if((rc = read_vtpmblk_raw(&cipher, &cipher_len))) {
>> +   active_slot = 0;
>> +   /* Read slot 0 from disk now */
>> +   if((rc = read_vtpmblk_raw(&cipher, &cipher_len, 0))) {
>> +      goto abort_egress;
>> +   }
>> +
>> +   /* Compute the hash of the cipher text and compare */
>> +   sha1(cipher, cipher_len, hash0);
>> +   if(!memcmp(hash0, hashkey, HASHSZ))
>> +      goto valid;
>> +
>> +   free(cipher);
>> +   cipher = NULL;
>> +
>> +   active_slot = 1;
>> +   /* Read slot 1 from disk now */
>> +   if((rc = read_vtpmblk_raw(&cipher, &cipher_len, 1))) {
>>         goto abort_egress;
>>      }
>>        /* Compute the hash of the cipher text and compare */
>> -   sha1(cipher, cipher_len, hash);
>> -   if(memcmp(hash, hashkey, HASHSZ)) {
>> +   sha1(cipher, cipher_len, hash1);
>> +   if(!memcmp(hash1, hashkey, HASHSZ))
>> +      goto valid;
>> +
>> +   {
>>         int i;
>>         error("NVM Storage Checksum failed!");
>>         printf("Expected: ");
>> @@ -285,14 +320,20 @@ int read_vtpmblk(struct tpmfront_dev* tpmfront_dev, 
>> uint8_t** data, size_t *data
>>        printf("%02hhX ", hashkey[i]);
>>         }
>>         printf("\n");
>> -      printf("Actual:   ");
>> +      printf("Slot 0:   ");
>> +      for(i = 0; i < HASHSZ; ++i) {
>> +     printf("%02hhX ", hash0[i]);
>> +      }
>> +      printf("\n");
>> +      printf("Slot 1:   ");
>>         for(i = 0; i < HASHSZ; ++i) {
>> -     printf("%02hhX ", hash[i]);
>> +     printf("%02hhX ", hash1[i]);
>>         }
>>         printf("\n");
>>         rc = -1;
>>         goto abort_egress;
>>      }
>> +valid:
>>        /* Decrypt the blob */
>>      if((rc = decrypt_vtpmblk(cipher, cipher_len, data, data_length, 
>> symkey))) {
>> @@ -300,6 +341,7 @@ int read_vtpmblk(struct tpmfront_dev* tpmfront_dev, 
>> uint8_t** data, size_t *data
>>      }
>>      goto egress;
>>   abort_egress:
>> +   active_slot = -1;
> See my comment above
>>   egress:
>>      free(cipher);
>>      free(hashkey);
> 
> 


-- 
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®.