diff options
author | Igor Pashev <pashev.igor@gmail.com> | 2012-11-25 14:36:20 +0000 |
---|---|---|
committer | Igor Pashev <pashev.igor@gmail.com> | 2012-11-25 14:36:20 +0000 |
commit | c3649a2def02c41d837ae1f79dda729ccb91e677 (patch) | |
tree | bea46dff212fdef977fe9094a70a939e8cc21885 /src/tcs/ps/ps_utils.c | |
download | trousers-upstream/0.3.9.tar.gz |
Imported Upstream version 0.3.9upstream/0.3.9upstream
Diffstat (limited to 'src/tcs/ps/ps_utils.c')
-rw-r--r-- | src/tcs/ps/ps_utils.c | 538 |
1 files changed, 538 insertions, 0 deletions
diff --git a/src/tcs/ps/ps_utils.c b/src/tcs/ps/ps_utils.c new file mode 100644 index 0000000..2e7f502 --- /dev/null +++ b/src/tcs/ps/ps_utils.c @@ -0,0 +1,538 @@ +/* + * Licensed Materials - Property of IBM + * + * trousers - An open source TCG Software Stack + * + * (C) Copyright International Business Machines Corp. 2004 + * + */ + + +#include <stdlib.h> +#include <stdio.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/stat.h> +#if defined(HAVE_BYTEORDER_H) +#include <sys/byteorder.h> +#elif defined(HTOLE_DEFINED) +#include <endian.h> +#define LE_16 htole16 +#define LE_32 htole32 +#define LE_64 htole64 +#else +#define LE_16(x) (x) +#define LE_32(x) (x) +#define LE_64(x) (x) +#endif +#include <fcntl.h> +#include <string.h> +#include <limits.h> +#include <assert.h> +#include <errno.h> + +#include "trousers/tss.h" +#include "trousers_types.h" +#include "tcs_int_literals.h" +#include "tcsps.h" +#include "tcs_tsp.h" +#include "tcs_utils.h" +#include "tcslog.h" + +struct key_disk_cache *key_disk_cache_head = NULL; + + +#ifdef SOLARIS +TSS_RESULT +#else +inline TSS_RESULT +#endif +read_data(int fd, void *data, UINT32 size) +{ + int rc; + + rc = read(fd, data, size); + if (rc == -1) { + LogError("read of %d bytes: %s", size, strerror(errno)); + return TCSERR(TSS_E_INTERNAL_ERROR); + } else if ((unsigned)rc != size) { + LogError("read of %d bytes (only %d read)", size, rc); + return TCSERR(TSS_E_INTERNAL_ERROR); + } + + return TSS_SUCCESS; +} + + +#ifdef SOLARIS +TSS_RESULT +#else +inline TSS_RESULT +#endif +write_data(int fd, void *data, UINT32 size) +{ + int rc; + + rc = write(fd, data, size); + if (rc == -1) { + LogError("write of %d bytes: %s", size, strerror(errno)); + return TCSERR(TSS_E_INTERNAL_ERROR); + } else if ((unsigned)rc != size) { + LogError("write of %d bytes (only %d written)", size, rc); + return TCSERR(TSS_E_INTERNAL_ERROR); + } + + return TSS_SUCCESS; +} + +/* + * called by write_key_init to find the next available location in the PS file to + * write a new key to. + */ +int +find_write_offset(UINT32 pub_data_size, UINT32 blob_size, UINT32 vendor_data_size) +{ + struct key_disk_cache *tmp; + unsigned int offset; + + MUTEX_LOCK(disk_cache_lock); + + tmp = key_disk_cache_head; + while (tmp) { + /* if we find a deleted key of the right size, return its offset */ + if (!(tmp->flags & CACHE_FLAG_VALID) && + tmp->pub_data_size == pub_data_size && + tmp->blob_size == blob_size && + tmp->vendor_data_size == vendor_data_size) { + offset = tmp->offset; + MUTEX_UNLOCK(disk_cache_lock); + return offset; + } + tmp = tmp->next; + } + + MUTEX_UNLOCK(disk_cache_lock); + + /* no correctly sized holes */ + return -1; +} + +/* + * move the file pointer to the point where the next key can be written and return + * that offset + */ +int +write_key_init(int fd, UINT32 pub_data_size, UINT32 blob_size, UINT32 vendor_data_size) +{ + UINT32 num_keys; + BYTE version; + int rc, offset; + + /* seek to the PS version */ + rc = lseek(fd, TSSPS_VERSION_OFFSET, SEEK_SET); + if (rc == ((off_t) - 1)) { + LogError("lseek: %s", strerror(errno)); + return -1; + } + + /* go to NUM_KEYS */ + rc = lseek(fd, TSSPS_NUM_KEYS_OFFSET, SEEK_SET); + if (rc == ((off_t) - 1)) { + LogError("lseek: %s", strerror(errno)); + return -1; + } + + /* read the number of keys */ + rc = read(fd, &num_keys, sizeof(UINT32)); + num_keys = LE_32(num_keys); + if (rc == -1) { + LogError("read of %zd bytes: %s", sizeof(UINT32), strerror(errno)); + return -1; + } else if (rc == 0) { + /* This is the first key being written */ + num_keys = 1; + version = 1; + + /* seek to the PS version */ + rc = lseek(fd, TSSPS_VERSION_OFFSET, SEEK_SET); + if (rc == ((off_t) - 1)) { + LogError("lseek: %s", strerror(errno)); + return -1; + } + + /* write out the version info byte */ + if ((rc = write_data(fd, &version, sizeof(BYTE)))) { + LogError("%s", __FUNCTION__); + return rc; + } + + rc = lseek(fd, TSSPS_NUM_KEYS_OFFSET, SEEK_SET); + if (rc == ((off_t) - 1)) { + LogError("lseek: %s", strerror(errno)); + return -1; + } + + num_keys = LE_32(num_keys); + if ((rc = write_data(fd, &num_keys, sizeof(UINT32)))) { + LogError("%s", __FUNCTION__); + return rc; + } + + /* return the offset */ + return (TSSPS_NUM_KEYS_OFFSET + sizeof(UINT32)); + } + + /* if there is a hole in the file we can write to, find it */ + offset = find_write_offset(pub_data_size, blob_size, vendor_data_size); + + if (offset != -1) { + /* we found a hole, seek to it and don't increment the # of keys on disk */ + rc = lseek(fd, offset, SEEK_SET); + } else { + /* we didn't find a hole, increment the number of keys on disk and seek + * to the end of the file + */ + num_keys++; + + /* go to the beginning */ + rc = lseek(fd, TSSPS_NUM_KEYS_OFFSET, SEEK_SET); + if (rc == ((off_t) - 1)) { + LogError("lseek: %s", strerror(errno)); + return -1; + } + num_keys = LE_32(num_keys); + if ((rc = write_data(fd, &num_keys, sizeof(UINT32)))) { + LogError("%s", __FUNCTION__); + return rc; + } + + rc = lseek(fd, 0, SEEK_END); + } + if (rc == ((off_t) - 1)) { + LogError("lseek: %s", strerror(errno)); + return -1; + } + + /* lseek returns the number of bytes of offset from the beginning of the file */ + return rc; +} + +/* + * add a new cache entry for a written key + */ +TSS_RESULT +cache_key(UINT32 offset, UINT16 flags, + TSS_UUID *uuid, TSS_UUID *parent_uuid, + UINT16 pub_data_size, UINT32 blob_size, + UINT32 vendor_data_size) +{ + struct key_disk_cache *tmp; + + MUTEX_LOCK(disk_cache_lock); + + tmp = key_disk_cache_head; + + for (; tmp; tmp = tmp->next) { + /* reuse an invalidated key cache entry */ + if (!(tmp->flags & CACHE_FLAG_VALID)) + goto fill_cache_entry; + } + + tmp = malloc(sizeof(struct key_disk_cache)); + if (tmp == NULL) { + LogError("malloc of %zd bytes failed.", sizeof(struct key_disk_cache)); + MUTEX_UNLOCK(disk_cache_lock); + return TCSERR(TSS_E_OUTOFMEMORY); + } + tmp->next = key_disk_cache_head; + key_disk_cache_head = tmp; + +fill_cache_entry: + tmp->offset = offset; +#ifdef TSS_DEBUG + if (offset == 0) + LogDebug("Storing key with file offset==0!!!"); +#endif + tmp->flags = flags; + tmp->blob_size = blob_size; + tmp->pub_data_size = pub_data_size; + tmp->vendor_data_size = vendor_data_size; + memcpy(&tmp->uuid, uuid, sizeof(TSS_UUID)); + memcpy(&tmp->parent_uuid, parent_uuid, sizeof(TSS_UUID)); + + MUTEX_UNLOCK(disk_cache_lock); + return TSS_SUCCESS; +} + +/* + * read into the PS file and return the number of keys + */ +int +get_num_keys_in_file(int fd) +{ + UINT32 num_keys; + int rc; + + /* go to the number of keys */ + rc = lseek(fd, TSSPS_NUM_KEYS_OFFSET, SEEK_SET); + if (rc == ((off_t) - 1)) { + LogError("lseek: %s", strerror(errno)); + return 0; + } + + rc = read(fd, &num_keys, sizeof(UINT32)); + if (rc < 0) { + LogError("read of %zd bytes: %s", sizeof(UINT32), strerror(errno)); + return 0; + } else if ((unsigned)rc < sizeof(UINT32)) { + num_keys = 0; + } + num_keys = LE_32(num_keys); + + return num_keys; +} + +/* + * count the number of valid keys in the cache + */ +int +get_num_keys() +{ + int num_keys = 0; + struct key_disk_cache *tmp; + + MUTEX_LOCK(disk_cache_lock); + + tmp = key_disk_cache_head; + + for (; tmp; tmp = tmp->next) { + if (tmp->flags & CACHE_FLAG_VALID) + num_keys++; + } + + MUTEX_UNLOCK(disk_cache_lock); + return num_keys; +} + +/* + * disk store format: + * + * TrouSerS 0.2.0 and before: + * Version 0: cached? + * [UINT32 num_keys_on_disk] + * [TSS_UUID uuid0 ] yes + * [TSS_UUID uuid_parent0 ] yes + * [UINT16 pub_data_size0 ] yes + * [UINT16 blob_size0 ] yes + * [UINT16 cache_flags0 ] yes + * [BYTE[] pub_data0 ] + * [BYTE[] blob0 ] + * [...] + * + * TrouSerS 0.2.1+ + * Version 1: cached? + * [BYTE PS version = '\1'] + * [UINT32 num_keys_on_disk ] + * [TSS_UUID uuid0 ] yes + * [TSS_UUID uuid_parent0 ] yes + * [UINT16 pub_data_size0 ] yes + * [UINT16 blob_size0 ] yes + * [UINT32 vendor_data_size0] yes + * [UINT16 cache_flags0 ] yes + * [BYTE[] pub_data0 ] + * [BYTE[] blob0 ] + * [BYTE[] vendor_data0 ] + * [...] + * + */ +/* + * read the PS file pointed to by fd and create a cache based on it + */ +int +init_disk_cache(int fd) +{ + UINT32 num_keys = get_num_keys_in_file(fd); + UINT16 i; + UINT64 tmp_offset; + int rc = 0, offset; + struct key_disk_cache *tmp, *prev = NULL; + BYTE srk_blob[2048]; + TSS_KEY srk_key; +#ifdef TSS_DEBUG + int valid_keys = 0; +#endif + + MUTEX_LOCK(disk_cache_lock); + + if (num_keys == 0) { + key_disk_cache_head = NULL; + MUTEX_UNLOCK(disk_cache_lock); + return 0; + } else { + key_disk_cache_head = tmp = calloc(1, sizeof(struct key_disk_cache)); + if (tmp == NULL) { + LogError("malloc of %zd bytes failed.", + sizeof(struct key_disk_cache)); + rc = -1; + goto err_exit; + } + } + + /* make sure the file pointer is where we expect, just after the number + * of keys on disk at the head of the file + */ + offset = lseek(fd, TSSPS_KEYS_OFFSET, SEEK_SET); + if (offset == ((off_t) - 1)) { + LogError("lseek: %s", strerror(errno)); + rc = -1; + goto err_exit; + } + + for (i=0; i<num_keys; i++) { + offset = lseek(fd, 0, SEEK_CUR); + if (offset == ((off_t) - 1)) { + LogError("lseek: %s", strerror(errno)); + rc = -1; + goto err_exit; + } + tmp->offset = offset; +#ifdef TSS_DEBUG + if (offset == 0) + LogDebug("Storing key with file offset==0!!!"); +#endif + /* read UUID */ + if ((rc = read_data(fd, (void *)&tmp->uuid, sizeof(TSS_UUID)))) { + LogError("%s", __FUNCTION__); + goto err_exit; + } + + /* read parent UUID */ + if ((rc = read_data(fd, (void *)&tmp->parent_uuid, sizeof(TSS_UUID)))) { + LogError("%s", __FUNCTION__); + goto err_exit; + } + + /* pub data size */ + if ((rc = read_data(fd, &tmp->pub_data_size, sizeof(UINT16)))) { + LogError("%s", __FUNCTION__); + goto err_exit; + } + tmp->pub_data_size = LE_16(tmp->pub_data_size); + + DBG_ASSERT(tmp->pub_data_size <= 2048 && tmp->pub_data_size > 0); + + /* blob size */ + if ((rc = read_data(fd, &tmp->blob_size, sizeof(UINT16)))) { + LogError("%s", __FUNCTION__); + goto err_exit; + } + tmp->blob_size = LE_16(tmp->blob_size); + DBG_ASSERT(tmp->blob_size <= 4096 && tmp->blob_size > 0); + + /* vendor data size */ + if ((rc = read_data(fd, &tmp->vendor_data_size, sizeof(UINT32)))) { + LogError("%s", __FUNCTION__); + goto err_exit; + } + tmp->vendor_data_size = LE_32(tmp->vendor_data_size); + + /* cache flags */ + if ((rc = read_data(fd, &tmp->flags, sizeof(UINT16)))) { + LogError("%s", __FUNCTION__); + goto err_exit; + } + tmp->flags = LE_16(tmp->flags); + +#ifdef TSS_DEBUG + if (tmp->flags & CACHE_FLAG_VALID) + valid_keys++; +#endif + /* fast forward over the pub key */ + offset = lseek(fd, tmp->pub_data_size, SEEK_CUR); + if (offset == ((off_t) - 1)) { + LogError("lseek: %s", strerror(errno)); + rc = -1; + goto err_exit; + } + + /* if this is the SRK, load it into memory, since its already loaded in + * the chip */ + if (!memcmp(&SRK_UUID, &tmp->uuid, sizeof(TSS_UUID))) { + /* read SRK blob from disk */ + if ((rc = read_data(fd, srk_blob, tmp->blob_size))) { + LogError("%s", __FUNCTION__); + goto err_exit; + } + + tmp_offset = 0; + if ((rc = UnloadBlob_TSS_KEY(&tmp_offset, srk_blob, &srk_key))) + goto err_exit; + /* add to the mem cache */ + if ((rc = mc_add_entry_init(SRK_TPM_HANDLE, SRK_TPM_HANDLE, &srk_key, + &SRK_UUID))) { + LogError("Error adding SRK to mem cache."); + destroy_key_refs(&srk_key); + goto err_exit; + } + destroy_key_refs(&srk_key); + } else { + /* fast forward over the blob */ + offset = lseek(fd, tmp->blob_size, SEEK_CUR); + if (offset == ((off_t) - 1)) { + LogError("lseek: %s", strerror(errno)); + rc = -1; + goto err_exit; + } + + /* fast forward over the vendor data */ + offset = lseek(fd, tmp->vendor_data_size, SEEK_CUR); + if (offset == ((off_t) - 1)) { + LogError("lseek: %s", strerror(errno)); + rc = -1; + goto err_exit; + } + } + + tmp->next = calloc(1, sizeof(struct key_disk_cache)); + if (tmp->next == NULL) { + LogError("malloc of %zd bytes failed.", + sizeof(struct key_disk_cache)); + rc = -1; + goto err_exit; + } + prev = tmp; + tmp = tmp->next; + } + + /* delete the dangling, unfilled cache entry */ + free(tmp); + prev->next = NULL; + rc = 0; + LogDebug("%s: found %d valid key(s) on disk.\n", __FUNCTION__, valid_keys); + +err_exit: + MUTEX_UNLOCK(disk_cache_lock); + return rc; +} + +int +close_disk_cache(int fd) +{ + struct key_disk_cache *tmp, *tmp_next; + + if (key_disk_cache_head == NULL) + return 0; + + MUTEX_LOCK(disk_cache_lock); + tmp = key_disk_cache_head; + + do { + tmp_next = tmp->next; + free(tmp); + tmp = tmp_next; + } while (tmp); + + MUTEX_UNLOCK(disk_cache_lock); + + return 0; +} |