diff options
Diffstat (limited to 'src/tcs/tcs_key_mem_cache.c')
-rw-r--r-- | src/tcs/tcs_key_mem_cache.c | 1175 |
1 files changed, 1175 insertions, 0 deletions
diff --git a/src/tcs/tcs_key_mem_cache.c b/src/tcs/tcs_key_mem_cache.c new file mode 100644 index 0000000..4db0259 --- /dev/null +++ b/src/tcs/tcs_key_mem_cache.c @@ -0,0 +1,1175 @@ + +/* + * Licensed Materials - Property of IBM + * + * trousers - An open source TCG Software Stack + * + * (C) Copyright International Business Machines Corp. 2004-2006 + * + */ + + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> +#include <errno.h> + +#include "trousers/tss.h" +#include "trousers_types.h" +#include "trousers_types.h" +#include "tcs_tsp.h" +#include "tcs_utils.h" +#include "tcs_int_literals.h" +#include "capabilities.h" +#include "tcslog.h" +#include "tcsps.h" +#include "req_mgr.h" + +#include "tcs_key_ps.h" + +/* + * mem_cache_lock will be responsible for protecting the key_mem_cache_head list. This is a + * TCSD global linked list of all keys which have been loaded into the TPM at some time. + */ +MUTEX_DECLARE_INIT(mem_cache_lock); + +/* + * tcs_keyhandle_lock is only used to make TCS keyhandle generation atomic for all TCSD + * threads. + */ +static MUTEX_DECLARE_INIT(tcs_keyhandle_lock); + +/* + * timestamp_lock is only used to make TCS key timestamp generation atomic for all TCSD + * threads. + */ +static MUTEX_DECLARE_INIT(timestamp_lock); + +TCS_KEY_HANDLE +getNextTcsKeyHandle() +{ + static TCS_KEY_HANDLE NextTcsKeyHandle = 0x22330000; + TCS_KEY_HANDLE ret; + + MUTEX_LOCK(tcs_keyhandle_lock); + + do { + ret = NextTcsKeyHandle++; + } while (NextTcsKeyHandle == SRK_TPM_HANDLE || NextTcsKeyHandle == NULL_TCS_HANDLE); + + MUTEX_UNLOCK(tcs_keyhandle_lock); + + return ret; +} + +UINT32 +getNextTimeStamp() +{ + static UINT32 time_stamp = 1; + UINT32 ret; + + MUTEX_LOCK(timestamp_lock); + ret = time_stamp++; + MUTEX_UNLOCK(timestamp_lock); + + return ret; +} + +/* only called from load key paths, so no locking */ +TCPA_STORE_PUBKEY * +mc_get_pub_by_slot(TCPA_KEY_HANDLE tpm_handle) +{ + struct key_mem_cache *tmp; + TCPA_STORE_PUBKEY *ret; + + if (tpm_handle == NULL_TPM_HANDLE) + return NULL; + + for (tmp = key_mem_cache_head; tmp; tmp = tmp->next) { + LogDebugFn("TCSD mem_cached handle: 0x%x", + tmp->tcs_handle); + if (tmp->tpm_handle == tpm_handle) { + ret = tmp->blob ? &tmp->blob->pubKey : NULL; + return ret; + } + } + LogDebugFn("returning NULL TCPA_STORE_PUBKEY"); + return NULL; +} + +/* only called from load key paths, so no locking */ +TCPA_STORE_PUBKEY * +mc_get_pub_by_handle(TCS_KEY_HANDLE tcs_handle) +{ + struct key_mem_cache *tmp; + TCPA_STORE_PUBKEY *ret; + + LogDebugFn("looking for 0x%x", tcs_handle); + + for (tmp = key_mem_cache_head; tmp; tmp = tmp->next) { + LogDebugFn("TCSD mem_cached handle: 0x%x", + tmp->tcs_handle); + if (tmp->tcs_handle == tcs_handle) { + ret = tmp->blob ? &tmp->blob->pubKey : NULL; + return ret; + } + } + + LogDebugFn("returning NULL TCPA_STORE_PUBKEY"); + return NULL; +} + +/* only called from load key paths, so no locking */ +TSS_RESULT +mc_set_parent_by_handle(TCS_KEY_HANDLE tcs_handle, TCS_KEY_HANDLE p_tcs_handle) +{ + struct key_mem_cache *tmp, *parent; + + /* find parent */ + for (tmp = key_mem_cache_head; tmp; tmp = tmp->next) { + LogDebug("TCSD mem_cached handle: 0x%x", tmp->tcs_handle); + if (tmp->tcs_handle == p_tcs_handle) { + parent = tmp; + break; + } + } + + /* didn't find parent */ + if (tmp == NULL) + goto done; + + /* set parent blob in child */ + for (tmp = key_mem_cache_head; tmp; tmp = tmp->next) { + if (tmp->tcs_handle == tcs_handle) { + tmp->parent = parent; + return TSS_SUCCESS; + } + } +done: + return TCSERR(TSS_E_FAIL); +} + +TCPA_RESULT +ensureKeyIsLoaded(TCS_CONTEXT_HANDLE hContext, TCS_KEY_HANDLE keyHandle, TCPA_KEY_HANDLE * keySlot) +{ + TCPA_RESULT result = TSS_SUCCESS; + TCPA_STORE_PUBKEY *myPub; + + LogDebugFn("0x%x", keyHandle); + + if (!ctx_has_key_loaded(hContext, keyHandle)) + return TCSERR(TCS_E_INVALID_KEY); + + MUTEX_LOCK(mem_cache_lock); + + *keySlot = mc_get_slot_by_handle(keyHandle); + LogDebug("keySlot is %08X", *keySlot); + if (*keySlot == NULL_TPM_HANDLE || isKeyLoaded(*keySlot) == FALSE) { + LogDebug("calling mc_get_pub_by_handle"); + if ((myPub = mc_get_pub_by_handle(keyHandle)) == NULL) { + LogDebug("Failed to find pub by handle"); + result = TCSERR(TCS_E_KM_LOADFAILED); + goto done; + } + + LogDebugFn("calling LoadKeyShim"); + if ((result = LoadKeyShim(hContext, myPub, NULL, keySlot))) { + LogDebug("Failed shim"); + goto done; + } + + if (*keySlot == NULL_TPM_HANDLE) { + LogDebug("Key slot is still invalid after ensureKeyIsLoaded"); + result = TCSERR(TCS_E_KM_LOADFAILED); + goto done; + } + } + mc_update_time_stamp(*keySlot); + +done: + MUTEX_UNLOCK(mem_cache_lock); + LogDebugFn("Exit"); + return result; +} + + +/* only called from load key paths, so no locking */ +TSS_UUID * +mc_get_uuid_by_pub(TCPA_STORE_PUBKEY *pub) +{ + TSS_UUID *ret; + struct key_mem_cache *tmp; + + for (tmp = key_mem_cache_head; tmp; tmp = tmp->next) { + LogDebugFn("TCSD mem_cached handle: 0x%x", tmp->tcs_handle); + if (tmp->blob && + tmp->blob->pubKey.keyLength == pub->keyLength && + !memcmp(tmp->blob->pubKey.key, pub->key, pub->keyLength)) { + ret = &tmp->uuid; + return ret; + } + } + + return NULL; +} + +TSS_RESULT +mc_get_handles_by_uuid(TSS_UUID *uuid, TCS_KEY_HANDLE *tcsHandle, TCPA_KEY_HANDLE *slot) +{ + struct key_mem_cache *tmp; + + for (tmp = key_mem_cache_head; tmp; tmp = tmp->next) { + if (!memcmp(&tmp->uuid, uuid, sizeof(TSS_UUID))) { + *tcsHandle = tmp->tcs_handle; + *slot = tmp->tpm_handle; + return TSS_SUCCESS; + } + } + + return TCSERR(TSS_E_FAIL); +} + +TCS_KEY_HANDLE +mc_get_handle_by_encdata(BYTE *encData) +{ + struct key_mem_cache *tmp; + TCS_KEY_HANDLE ret; + + MUTEX_LOCK(mem_cache_lock); + + for (tmp = key_mem_cache_head; tmp; tmp = tmp->next) { + LogDebugFn("TCSD mem_cached handle: 0x%x", tmp->tcs_handle); + if (!tmp->blob || tmp->blob->encSize == 0) + continue; + if (!memcmp(tmp->blob->encData, encData, tmp->blob->encSize)) { + ret = tmp->tcs_handle; + MUTEX_UNLOCK(mem_cache_lock); + return ret; + } + } + MUTEX_UNLOCK(mem_cache_lock); + return 0; +} + +TSS_RESULT +mc_update_encdata(BYTE *encData, BYTE *newEncData) +{ + struct key_mem_cache *tmp; + BYTE *tmp_enc_data; + + MUTEX_LOCK(mem_cache_lock); + + for (tmp = key_mem_cache_head; tmp; tmp = tmp->next) { + LogDebugFn("TCSD mem_cached handle: 0x%x", tmp->tcs_handle); + if (!tmp->blob || tmp->blob->encSize == 0) + continue; + if (!memcmp(tmp->blob->encData, encData, tmp->blob->encSize)) { + tmp_enc_data = (BYTE *)malloc(tmp->blob->encSize); + if (tmp_enc_data == NULL) { + LogError("malloc of %u bytes failed.", tmp->blob->encSize); + MUTEX_UNLOCK(mem_cache_lock); + return TCSERR(TSS_E_OUTOFMEMORY); + } + + memcpy(tmp_enc_data, newEncData, tmp->blob->encSize); + free(tmp->blob->encData); + tmp->blob->encData = tmp_enc_data; + MUTEX_UNLOCK(mem_cache_lock); + return TSS_SUCCESS; + } + } + MUTEX_UNLOCK(mem_cache_lock); + LogError("Couldn't find requested encdata in mem cache"); + return TCSERR(TSS_E_INTERNAL_ERROR); +} + +/* + * only called from load key paths and the init (single thread time) path, + * so no locking + */ +TSS_RESULT +mc_add_entry(TCS_KEY_HANDLE tcs_handle, + TCPA_KEY_HANDLE tpm_handle, + TSS_KEY *key_blob) +{ + struct key_mem_cache *entry, *tmp; + + /* Make sure the cache doesn't already have an entry for this key */ + for (tmp = key_mem_cache_head; tmp; tmp = tmp->next) { + LogDebugFn("TCSD mem_cached handle: 0x%x", tmp->tcs_handle); + if (tcs_handle == tmp->tcs_handle) { + return TSS_SUCCESS; + } + } + + /* Not found - we need to create a new entry */ + entry = (struct key_mem_cache *)calloc(1, sizeof(struct key_mem_cache)); + if (entry == NULL) { + LogError("malloc of %zd bytes failed.", sizeof(struct key_mem_cache)); + return TCSERR(TSS_E_OUTOFMEMORY); + } + + entry->tcs_handle = tcs_handle; + if (tpm_handle != NULL_TPM_HANDLE) + entry->time_stamp = getNextTimeStamp(); + + entry->tpm_handle = tpm_handle; + + if (!key_blob) + goto add; + + /* allocate space for the blob */ + entry->blob = calloc(1, sizeof(TSS_KEY)); + if (entry->blob == NULL) { + LogError("malloc of %zd bytes failed.", sizeof(TSS_KEY)); + free(entry); + return TCSERR(TSS_E_OUTOFMEMORY); + } + memcpy(entry->blob, key_blob, sizeof(TSS_KEY)); + + /* allocate space for the key parameters if necessary */ + if (key_blob->algorithmParms.parmSize) { + BYTE *tmp_parms = (BYTE *)malloc(key_blob->algorithmParms.parmSize); + if (tmp_parms == NULL) { + LogError("malloc of %u bytes failed.", key_blob->algorithmParms.parmSize); + free(entry->blob); + free(entry); + return TCSERR(TSS_E_OUTOFMEMORY); + } + memcpy(tmp_parms, key_blob->algorithmParms.parms, key_blob->algorithmParms.parmSize); + entry->blob->algorithmParms.parms = tmp_parms; + } + entry->blob->algorithmParms.parmSize = key_blob->algorithmParms.parmSize; + + /* allocate space for the public key */ + if (key_blob->pubKey.keyLength > 0) { + entry->blob->pubKey.key = (BYTE *)malloc(key_blob->pubKey.keyLength); + if (entry->blob->pubKey.key == NULL) { + LogError("malloc of %u bytes failed.", key_blob->pubKey.keyLength); + free(entry->blob->algorithmParms.parms); + free(entry->blob); + free(entry); + return TCSERR(TSS_E_OUTOFMEMORY); + } + memcpy(entry->blob->pubKey.key, key_blob->pubKey.key, key_blob->pubKey.keyLength); + } + entry->blob->pubKey.keyLength = key_blob->pubKey.keyLength; + + /* allocate space for the PCR info */ + if (key_blob->PCRInfoSize > 0) { + entry->blob->PCRInfo = (BYTE *)malloc(key_blob->PCRInfoSize); + if (entry->blob->PCRInfo == NULL) { + LogError("malloc of %u bytes failed.", key_blob->PCRInfoSize); + free(entry->blob->pubKey.key); + free(entry->blob->algorithmParms.parms); + free(entry->blob); + free(entry); + return TCSERR(TSS_E_OUTOFMEMORY); + } + memcpy(entry->blob->PCRInfo, key_blob->PCRInfo, key_blob->PCRInfoSize); + } + entry->blob->PCRInfoSize = key_blob->PCRInfoSize; + + /* allocate space for the encData if necessary */ + if (key_blob->encSize > 0) { + entry->blob->encData = (BYTE *)malloc(key_blob->encSize); + if (entry->blob->encData == NULL) { + LogError("malloc of %u bytes failed.", key_blob->encSize); + free(entry->blob->PCRInfo); + free(entry->blob->pubKey.key); + free(entry->blob->algorithmParms.parms); + free(entry->blob); + free(entry); + return TCSERR(TSS_E_OUTOFMEMORY); + } + memcpy(entry->blob->encData, key_blob->encData, key_blob->encSize); + } + entry->blob->encSize = key_blob->encSize; +add: + /* add to the front of the list */ + entry->next = key_mem_cache_head; + if (key_mem_cache_head) { + /* set the reference count to 0 initially for all keys not being the SRK. Up + * the call chain, a reference to this mem cache entry will be set in the + * context object of the calling context and this reference count will be + * incremented there. */ + entry->ref_cnt = 0; + + key_mem_cache_head->prev = entry; + } else { + /* if we are the SRK, initially set the reference count to 1, so that it is + * always seen as loaded in the TPM. */ + entry->ref_cnt = 1; + } + key_mem_cache_head = entry; + + return TSS_SUCCESS; +} + +/* caller must lock the mem cache before calling! */ +TSS_RESULT +mc_remove_entry(TCS_KEY_HANDLE tcs_handle) +{ + struct key_mem_cache *cur; + + for (cur = key_mem_cache_head; cur; cur = cur->next) { + if (cur->tcs_handle == tcs_handle) { + if (cur->blob) { + destroy_key_refs(cur->blob); + free(cur->blob); + } + + if (cur->prev != NULL) + cur->prev->next = cur->next; + if (cur->next != NULL) + cur->next->prev = cur->prev; + + if (cur == key_mem_cache_head) + key_mem_cache_head = cur->next; + free(cur); + + return TSS_SUCCESS; + } + } + + return TCSERR(TSS_E_FAIL); +} + +TSS_RESULT +mc_add_entry_init(TCS_KEY_HANDLE tcs_handle, + TCPA_KEY_HANDLE tpm_handle, + TSS_KEY *key_blob, + TSS_UUID *uuid) +{ + struct key_mem_cache *entry, *tmp; + + /* Make sure the cache doesn't already have an entry for this key */ + MUTEX_LOCK(mem_cache_lock); + for (tmp = key_mem_cache_head; tmp; tmp = tmp->next) { + if (tcs_handle == tmp->tcs_handle) { + mc_remove_entry(tcs_handle); + } + } + MUTEX_UNLOCK(mem_cache_lock); + + /* Not found - we need to create a new entry */ + entry = (struct key_mem_cache *)calloc(1, sizeof(struct key_mem_cache)); + if (entry == NULL) { + LogError("malloc of %zd bytes failed.", sizeof(struct key_mem_cache)); + return TCSERR(TSS_E_OUTOFMEMORY); + } + + entry->tcs_handle = tcs_handle; + if (tpm_handle != NULL_TPM_HANDLE) + entry->time_stamp = getNextTimeStamp(); + + entry->tpm_handle = tpm_handle; + + if (key_blob) { + /* allocate space for the blob */ + entry->blob = malloc(sizeof(TSS_KEY)); + if (entry->blob == NULL) { + LogError("malloc of %zd bytes failed.", sizeof(TSS_KEY)); + free(entry); + return TCSERR(TSS_E_OUTOFMEMORY); + } + + memcpy(entry->blob, key_blob, sizeof(TSS_KEY)); + + /* allocate space for the key parameters if necessary */ + if (key_blob->algorithmParms.parmSize) { + BYTE *tmp_parms = (BYTE *)malloc(key_blob->algorithmParms.parmSize); + if (tmp_parms == NULL) { + LogError("malloc of %u bytes failed.", + key_blob->algorithmParms.parmSize); + free(entry->blob); + free(entry); + return TCSERR(TSS_E_OUTOFMEMORY); + } + memcpy(tmp_parms, key_blob->algorithmParms.parms, + key_blob->algorithmParms.parmSize); + entry->blob->algorithmParms.parms = tmp_parms; + } + + /* allocate space for the public key */ + entry->blob->pubKey.key = (BYTE *)malloc(key_blob->pubKey.keyLength); + if (entry->blob->pubKey.key == NULL) { + LogError("malloc of %u bytes failed.", key_blob->pubKey.keyLength); + free(entry->blob); + free(entry); + return TCSERR(TSS_E_OUTOFMEMORY); + } + memcpy(entry->blob->pubKey.key, key_blob->pubKey.key, key_blob->pubKey.keyLength); + + /* allocate space for the encData if necessary */ + if (key_blob->encSize != 0) { + entry->blob->encData = (BYTE *)malloc(key_blob->encSize); + if (entry->blob->encData == NULL) { + LogError("malloc of %u bytes failed.", key_blob->encSize); + free(entry->blob->pubKey.key); + free(entry->blob); + free(entry); + return TCSERR(TSS_E_OUTOFMEMORY); + } + memcpy(entry->blob->encData, key_blob->encData, key_blob->encSize); + } + entry->blob->encSize = key_blob->encSize; + } + + memcpy(&entry->uuid, uuid, sizeof(TSS_UUID)); + + MUTEX_LOCK(mem_cache_lock); + + entry->next = key_mem_cache_head; + if (key_mem_cache_head) + key_mem_cache_head->prev = entry; + + entry->ref_cnt = 1; + key_mem_cache_head = entry; + MUTEX_UNLOCK(mem_cache_lock); + + return TSS_SUCCESS; +} + +/* only called from evict key paths, so no locking */ +TSS_RESULT +mc_set_slot_by_slot(TCPA_KEY_HANDLE old_handle, TCPA_KEY_HANDLE new_handle) +{ + struct key_mem_cache *tmp; + + for (tmp = key_mem_cache_head; tmp; tmp = tmp->next) { + if (tmp->tpm_handle == old_handle) { + LogDebugFn("Set TCS key 0x%x, old TPM handle: 0x%x " + "new TPM handle: 0x%x", tmp->tcs_handle, + old_handle, new_handle); + if (new_handle == NULL_TPM_HANDLE) + tmp->time_stamp = 0; + else + tmp->time_stamp = getNextTimeStamp(); + tmp->tpm_handle = new_handle; + return TSS_SUCCESS; + } + } + + return TCSERR(TSS_E_FAIL); +} + +/* only called from load key paths, so no locking */ +TSS_RESULT +mc_set_slot_by_handle(TCS_KEY_HANDLE tcs_handle, TCPA_KEY_HANDLE tpm_handle) +{ + struct key_mem_cache *tmp; + + for (tmp = key_mem_cache_head; tmp; tmp = tmp->next) { + LogDebug("TCSD mem_cached handle: 0x%x", tmp->tcs_handle); + if (tmp->tcs_handle == tcs_handle) { + if (tpm_handle == NULL_TPM_HANDLE) + tmp->time_stamp = 0; + else + tmp->time_stamp = getNextTimeStamp(); + tmp->tpm_handle = tpm_handle; + return TSS_SUCCESS; + } + } + + return TCSERR(TSS_E_FAIL); +} + +/* the beginnings of a key manager start here ;-) */ + +TSS_RESULT +key_mgr_evict(TCS_CONTEXT_HANDLE hContext, TCS_KEY_HANDLE hKey) +{ + TSS_RESULT result = TCS_SUCCESS; + + if ((result = ctx_remove_key_loaded(hContext, hKey))) + return result; + + if ((result = key_mgr_dec_ref_count(hKey))) + return result; + + key_mgr_ref_count(); + + return result; +} + +TSS_RESULT +key_mgr_load_by_blob(TCS_CONTEXT_HANDLE hContext, TCS_KEY_HANDLE hUnwrappingKey, + UINT32 cWrappedKeyBlob, BYTE *rgbWrappedKeyBlob, + TPM_AUTH *pAuth, TCS_KEY_HANDLE *phKeyTCSI, TCS_KEY_HANDLE *phKeyHMAC) +{ + TSS_RESULT result; + + /* Check that auth for the parent key is loaded outside the mem_cache_lock. We have to do + * this here because if the TPM can't process this request right now, the thread could be + * put to sleep while holding the mem_cache_lock, which would result in a deadlock */ + if (pAuth) { + if ((result = auth_mgr_check(hContext, &pAuth->AuthHandle))) + return result; + } + + MUTEX_LOCK(mem_cache_lock); + + if (TPM_VERSION_IS(1,2)) { + result = TCSP_LoadKey2ByBlob_Internal(hContext, hUnwrappingKey, cWrappedKeyBlob, + rgbWrappedKeyBlob, pAuth, phKeyTCSI); + } else { + result = TCSP_LoadKeyByBlob_Internal(hContext, hUnwrappingKey, cWrappedKeyBlob, + rgbWrappedKeyBlob, pAuth, phKeyTCSI, + phKeyHMAC); + } + + MUTEX_UNLOCK(mem_cache_lock); + + return result; +} + +/* create a reference to one key. This is called from the key_mgr_load_* + * functions only, so no locking is done. + */ +TSS_RESULT +key_mgr_inc_ref_count(TCS_KEY_HANDLE key_handle) +{ + struct key_mem_cache *cur; + + for (cur = key_mem_cache_head; cur; cur = cur->next) { + LogDebugFn("TCSD mem_cached handle: 0x%x", cur->tcs_handle); + if (cur->tcs_handle == key_handle) { + cur->ref_cnt++; + return TSS_SUCCESS; + } + } + + return TCSERR(TSS_E_FAIL); +} + +/* de-reference one key. This is called by the context routines, so + * locking is necessary. + */ +TSS_RESULT +key_mgr_dec_ref_count(TCS_KEY_HANDLE key_handle) +{ + struct key_mem_cache *cur; + + MUTEX_LOCK(mem_cache_lock); + + for (cur = key_mem_cache_head; cur; cur = cur->next) { + if (cur->tcs_handle == key_handle) { + cur->ref_cnt--; + LogDebugFn("decrementing ref cnt for key 0x%x", + key_handle); + MUTEX_UNLOCK(mem_cache_lock); + return TSS_SUCCESS; + } + } + + MUTEX_UNLOCK(mem_cache_lock); + return TCSERR(TSS_E_FAIL); +} + +/* run through the global list and free any keys with reference counts of 0 */ +void +key_mgr_ref_count() +{ + struct key_mem_cache *tmp, *cur; + + MUTEX_LOCK(mem_cache_lock); + + for (cur = key_mem_cache_head; cur;) { + if (cur->ref_cnt == 0) { + if (cur->tpm_handle != NULL_TPM_HANDLE) { + LogDebugFn("Key 0x%x being freed from TPM", cur->tpm_handle); + internal_EvictByKeySlot(cur->tpm_handle); + } + LogDebugFn("Key 0x%x being freed", cur->tcs_handle); + if (cur->blob) { + destroy_key_refs(cur->blob); + free(cur->blob); + } + if (cur->prev != NULL) + cur->prev->next = cur->next; + if (cur->next != NULL) + cur->next->prev = cur->prev; + + tmp = cur; + if (cur == key_mem_cache_head) + key_mem_cache_head = cur->next; + cur = cur->next; + free(tmp); + } else { + cur = cur->next; + } + } + + MUTEX_UNLOCK(mem_cache_lock); +} + +/* only called from load key paths, so no locking */ +TCPA_KEY_HANDLE +mc_get_slot_by_handle(TCS_KEY_HANDLE tcs_handle) +{ + struct key_mem_cache *tmp; + TCS_KEY_HANDLE ret; + + for (tmp = key_mem_cache_head; tmp; tmp = tmp->next) { + LogDebugFn("TCSD mem_cached handle: 0x%x", tmp->tcs_handle); + if (tmp->tcs_handle == tcs_handle) { + ret = tmp->tpm_handle; + return ret; + } + } + + LogDebugFn("returning NULL_TPM_HANDLE"); + return NULL_TPM_HANDLE; +} + +/* called from functions outside the load key path */ +TCPA_KEY_HANDLE +mc_get_slot_by_handle_lock(TCS_KEY_HANDLE tcs_handle) +{ + struct key_mem_cache *tmp; + TCS_KEY_HANDLE ret; + + MUTEX_LOCK(mem_cache_lock); + + for (tmp = key_mem_cache_head; tmp; tmp = tmp->next) { + LogDebugFn("TCSD mem_cached handle: 0x%x", tmp->tcs_handle); + if (tmp->tcs_handle == tcs_handle) { + ret = tmp->tpm_handle; + MUTEX_UNLOCK(mem_cache_lock); + return ret; + } + } + + MUTEX_UNLOCK(mem_cache_lock); + LogDebugFn("returning NULL_TPM_HANDLE"); + return NULL_TPM_HANDLE; +} + +/* only called from load key paths, so no locking */ +TCPA_KEY_HANDLE +mc_get_slot_by_pub(TCPA_STORE_PUBKEY *pub) +{ + struct key_mem_cache *tmp; + TCPA_KEY_HANDLE ret; + + for (tmp = key_mem_cache_head; tmp; tmp = tmp->next) { + LogDebugFn("TCSD mem_cached handle: 0x%x", tmp->tcs_handle); + if (tmp->blob && + !memcmp(tmp->blob->pubKey.key, pub->key, pub->keyLength)) { + ret = tmp->tpm_handle; + return ret; + } + } + + LogDebugFn("returning NULL_TPM_HANDLE"); + return NULL_TPM_HANDLE; +} + +/* Check the mem cache for a key with public key pub. If a parent TCS key handle + * is passed in, make sure the parent of the key find matches it, else return + * key not found */ +/* only called from load key paths, so no locking */ +TCS_KEY_HANDLE +mc_get_handle_by_pub(TCPA_STORE_PUBKEY *pub, TCS_KEY_HANDLE parent) +{ + struct key_mem_cache *tmp; + + for (tmp = key_mem_cache_head; tmp; tmp = tmp->next) { + LogDebugFn("TCSD mem_cached handle: 0x%x", tmp->tcs_handle); + if (tmp->blob && + pub->keyLength == tmp->blob->pubKey.keyLength && + !memcmp(tmp->blob->pubKey.key, pub->key, pub->keyLength)) { + if (parent) { + if (!tmp->parent) + continue; + if (parent == tmp->parent->tcs_handle) + return tmp->tcs_handle; + } else + return tmp->tcs_handle; + } + } + + LogDebugFn("returning NULL_TCS_HANDLE"); + return NULL_TCS_HANDLE; +} + +/* only called from load key paths, so no locking */ +TCPA_STORE_PUBKEY * +mc_get_parent_pub_by_pub(TCPA_STORE_PUBKEY *pub) +{ + struct key_mem_cache *tmp; + TCPA_STORE_PUBKEY *ret = NULL; + + for (tmp = key_mem_cache_head; tmp; tmp = tmp->next) { + LogDebugFn("TCSD mem_cached handle: 0x%x", tmp->tcs_handle); + if (tmp->tcs_handle == TPM_KEYHND_SRK) { + LogDebugFn("skipping the SRK"); + continue; + } + if (tmp->blob && + !memcmp(tmp->blob->pubKey.key, pub->key, pub->keyLength)) { + if (tmp->parent && tmp->parent->blob) { + ret = &tmp->parent->blob->pubKey; + LogDebugFn("Success"); + } else { + LogError("parent pointer not set in key mem cache object w/ TCS " + "handle: 0x%x", tmp->tcs_handle); + } + return ret; + } + } + + LogDebugFn("returning NULL TCPA_STORE_PUBKEY"); + return NULL; +} + +/* only called from load key paths, so no locking */ +TSS_RESULT +mc_get_blob_by_pub(TCPA_STORE_PUBKEY *pub, TSS_KEY **ret_key) +{ + struct key_mem_cache *tmp; + + for (tmp = key_mem_cache_head; tmp; tmp = tmp->next) { + LogDebugFn("TCSD mem_cached handle: 0x%x", tmp->tcs_handle); + if (tmp->blob && + !memcmp(tmp->blob->pubKey.key, pub->key, pub->keyLength)) { + *ret_key = tmp->blob; + return TSS_SUCCESS; + } + } + + LogDebugFn("returning TSS_E_FAIL"); + return TCSERR(TSS_E_FAIL); +} + +/* only called from load key paths, so no locking */ +TCS_KEY_HANDLE +mc_get_handle_by_slot(TCPA_KEY_HANDLE tpm_handle) +{ + struct key_mem_cache *tmp; + TCS_KEY_HANDLE ret; + + for (tmp = key_mem_cache_head; tmp; tmp = tmp->next) { + LogDebugFn("TCSD mem_cached handle: 0x%x", tmp->tcs_handle); + if (tmp->tpm_handle == tpm_handle) { + ret = tmp->tcs_handle; + return ret; + } + } + + return NULL_TCS_HANDLE; +} + +/* only called from load key paths, so no locking */ +TSS_RESULT +mc_update_time_stamp(TCPA_KEY_HANDLE tpm_handle) +{ + struct key_mem_cache *tmp; + + for (tmp = key_mem_cache_head; tmp; tmp = tmp->next) { + LogDebugFn("TCSD mem_cached handle: 0x%x", tmp->tcs_handle); + if (tmp->tpm_handle == tpm_handle) { + tmp->time_stamp = getNextTimeStamp(); + return TSS_SUCCESS; + } + } + + return TCSERR(TSS_E_FAIL); +} + +/* Right now this evicts the LRU key assuming it's not the parent */ +TSS_RESULT +evictFirstKey(TCS_KEY_HANDLE parent_tcs_handle) +{ + struct key_mem_cache *tmp; + TCS_KEY_HANDLE tpm_handle_to_evict = NULL_TPM_HANDLE; + UINT32 smallestTimeStamp = ~(0U); /* largest */ + TSS_RESULT result; + UINT32 count; + + /* First, see if there are any known keys worth evicting */ + if ((result = clearUnknownKeys(InternalContext, &count))) + return result; + + if (count > 0) { + LogDebugFn("Evicted %u unknown keys", count); + return TSS_SUCCESS; + } + + for (tmp = key_mem_cache_head; tmp; tmp = tmp->next) { + if (tmp->tpm_handle != NULL_TPM_HANDLE && /* not already evicted */ + tmp->tpm_handle != SRK_TPM_HANDLE && /* not the srk */ + tmp->tcs_handle != parent_tcs_handle && /* not my parent */ + tmp->time_stamp < smallestTimeStamp) { /* is the smallest time + stamp so far */ + tpm_handle_to_evict = tmp->tpm_handle; + smallestTimeStamp = tmp->time_stamp; + } + } + + if (tpm_handle_to_evict != NULL_TCS_HANDLE) { + if ((result = internal_EvictByKeySlot(tpm_handle_to_evict))) + return result; + + LogDebugFn("Evicted key w/ TPM handle 0x%x", tpm_handle_to_evict); + result = mc_set_slot_by_slot(tpm_handle_to_evict, NULL_TPM_HANDLE); + } else + return TSS_SUCCESS; + + return result; +} + +TSS_BOOL +isKeyLoaded(TCPA_KEY_HANDLE keySlot) +{ + UINT64 offset; + UINT32 i; + TCPA_KEY_HANDLE_LIST keyList; + UINT32 respSize; + BYTE *resp; + TSS_RESULT result; + + if (keySlot == SRK_TPM_HANDLE) { + return TRUE; + } + + if ((result = TCSP_GetCapability_Internal(InternalContext, TCPA_CAP_KEY_HANDLE, 0, NULL, + &respSize, &resp))) + goto not_loaded; + + offset = 0; + UnloadBlob_KEY_HANDLE_LIST(&offset, resp, &keyList); + free(resp); + for (i = 0; i < keyList.loaded; i++) { + LogDebugFn("loaded TPM key handle: 0x%x", keyList.handle[i]); + if (keyList.handle[i] == keySlot) { + free(keyList.handle); + return TRUE; + } + } + + free(keyList.handle); + +not_loaded: + LogDebugFn("Key is not loaded, changing slot"); + mc_set_slot_by_slot(keySlot, NULL_TPM_HANDLE); + return FALSE; +} + +/* all calls to LoadKeyShim are inside locks */ +TSS_RESULT +LoadKeyShim(TCS_CONTEXT_HANDLE hContext, TCPA_STORE_PUBKEY *pubKey, + TSS_UUID *parentUuid, TCPA_KEY_HANDLE *slotOut) +{ + + TCPA_STORE_PUBKEY *parentPub; + UINT32 result; + TCPA_KEY_HANDLE keySlot; + TCPA_KEY_HANDLE parentSlot; + TCS_KEY_HANDLE tcsKeyHandle; + TSS_KEY *myKey; + UINT64 offset; + TCS_KEY_HANDLE parentHandle; + BYTE keyBlob[1024]; + + LogDebugFn("calling mc_get_slot_by_pub"); + + /* If I'm loaded, then no point being here. Get the slot and return */ + keySlot = mc_get_slot_by_pub(pubKey); + if (keySlot != NULL_TPM_HANDLE && isKeyLoaded(keySlot)) { + *slotOut = keySlot; + return TSS_SUCCESS; + } + + /* + * Before proceeding, the parent must be loaded. + * If the parent is registered, then it can be loaded by UUID. + * If not, then the shim will be called to load it's parent and then try + * to load it based on the persistent store. + */ + + LogDebugFn("calling mc_get_parent_pub_by_pub"); + /* Check if the Key is in the memory cache */ + if ((parentPub = mc_get_parent_pub_by_pub(pubKey)) == NULL) { +#if 0 + LogDebugFn("parentPub is NULL"); + /* If parentUUID is not handed in, then this key was never loaded and isn't reg'd */ + if (parentUuid == NULL) + return TCSERR(TCS_E_KM_LOADFAILED); + + LogDebugFn("calling TCSP_LoadKeyByUUID_Internal"); + /* This will try to load my parent by UUID */ + if ((result = TCSP_LoadKeyByUUID_Internal(hContext, parentUuid, NULL, &parentSlot))) + return result; +#else + return TCSERR(TCS_E_KM_LOADFAILED); +#endif + } else { + LogDebugFn("calling LoadKeyShim"); + if ((result = LoadKeyShim(hContext, parentPub, NULL, &parentSlot))) + return result; + } + + /* + * Now that the parent is loaded, I can load myself. + * If I'm registered, that's by UUID. If I'm not, + * that's by blob. If there is no persistent storage data, then I cannot be + * loaded by blob. The user must have some point loaded this key manually. + */ + + /* check the mem cache */ + if (mc_get_blob_by_pub(pubKey, &myKey) == TSS_SUCCESS) { + parentHandle = mc_get_handle_by_slot(parentSlot); + if (parentHandle == 0) + return TCSERR(TCS_E_KM_LOADFAILED); + + offset = 0; + LoadBlob_TSS_KEY(&offset, keyBlob, myKey); + if (TPM_VERSION_IS(1,2)) + result = TCSP_LoadKey2ByBlob_Internal(hContext, + parentHandle, offset, + keyBlob, NULL, + &tcsKeyHandle); + else + result = TCSP_LoadKeyByBlob_Internal(hContext, + parentHandle, offset, + keyBlob, NULL, + &tcsKeyHandle, slotOut); + if (result) + return result; + + return ctx_mark_key_loaded(hContext, tcsKeyHandle); +#if TSS_BUILD_PS + } else { + TSS_UUID *uuid; + + /* check registered */ + if (ps_is_pub_registered(pubKey) == FALSE) + return TCSERR(TCS_E_KM_LOADFAILED); + //uuid = mc_get_uuid_by_pub(pubKey); // XXX pub is not in MC + if ((result = ps_get_uuid_by_pub(pubKey, &uuid))) + return result; + + if ((result = TCSP_LoadKeyByUUID_Internal(hContext, uuid, NULL, &tcsKeyHandle))) { + free(uuid); + return result; + } + free(uuid); + *slotOut = mc_get_slot_by_handle(tcsKeyHandle); + + return ctx_mark_key_loaded(hContext, tcsKeyHandle); +#endif + } + + return TCSERR(TCS_E_KM_LOADFAILED); +} + +TSS_RESULT +owner_evict_init() +{ + TSS_RESULT result = TSS_SUCCESS; + TCPA_KEY_HANDLE_LIST keyList = { 0, NULL }; + BYTE *respData = NULL, ownerEvictCtr = 0; + UINT32 respDataSize = 0, i; + UINT64 offset = 0; + + /* If we're a 1.1 TPM, we can exit immediately since only 1.2+ supports owner evict */ + if (TPM_VERSION_IS(1,1)) + return TSS_SUCCESS; + + if ((result = TCSP_GetCapability_Internal(InternalContext, TPM_CAP_KEY_HANDLE, 0, NULL, + &respDataSize, &respData))) + return result; + + if ((result = UnloadBlob_KEY_HANDLE_LIST(&offset, respData, &keyList))) { + free(respData); + return result; + } + + free(respData); + for (i = 0; i < keyList.loaded; i++) { + UINT64 offset = 0; + UINT32 keyHandle; + + LoadBlob_UINT32(&offset, keyList.handle[i], (BYTE *)&keyHandle); + /* get the ownerEvict flag for this key handle */ + result = TCSP_GetCapability_Internal(InternalContext, TPM_CAP_KEY_STATUS, + sizeof(UINT32), (BYTE *)&keyHandle, + &respDataSize, &respData); + /* special case, invalid keys are automatically evicted later */ + if (result == TPM_E_INVALID_KEYHANDLE) + continue; + if (result != TSS_SUCCESS) { + free(keyList.handle); + return result; + } + + if (*(TPM_BOOL *)respData == TRUE) { + TSS_UUID uuid = TSS_UUID_OWNEREVICT(ownerEvictCtr); + + LogDebugFn("Found an owner evict key, assigned uuid %hhu", ownerEvictCtr); + if ((result = mc_add_entry_init(getNextTcsKeyHandle(), keyList.handle[i], + NULL, &uuid))) { + free(keyList.handle); + return result; + } + ownerEvictCtr++; + } + } + + return result; +} + +/* find next lowest OWNEREVICT uuid */ +TSS_RESULT +mc_find_next_ownerevict_uuid(TSS_UUID *uuid) +{ + TCS_KEY_HANDLE tmpKey; + TCPA_KEY_HANDLE tmpSlot; + UINT16 seed = 0; + TSS_RESULT result = TCSERR(TSS_E_FAIL); + + MUTEX_LOCK(mem_cache_lock); + + for (seed = 0; seed <= 255; seed++) { + TSS_UUID tmpUuid = TSS_UUID_OWNEREVICT(seed); + + /* if UUID is found, continue on, trying the next UUID */ + if (!mc_get_handles_by_uuid(&tmpUuid, &tmpKey, &tmpSlot)) + continue; + + /* UUID is not found, so its the first one available */ + memcpy(uuid, &tmpUuid, sizeof(TSS_UUID)); + result = TSS_SUCCESS; + break; + } + + MUTEX_UNLOCK(mem_cache_lock); + return result; +} + +TSS_RESULT +mc_set_uuid(TCS_KEY_HANDLE tcs_handle, TSS_UUID *uuid) +{ + struct key_mem_cache *tmp; + TSS_RESULT result = TCSERR(TSS_E_FAIL); + + MUTEX_LOCK(mem_cache_lock); + + LogDebugFn("looking for 0x%x", tcs_handle); + + for (tmp = key_mem_cache_head; tmp; tmp = tmp->next) { + LogDebugFn("TCSD mem_cached handle: 0x%x", tmp->tcs_handle); + if (tmp->tcs_handle == tcs_handle) { + LogDebugFn("Handle found, re-setting UUID"); + memcpy(&tmp->uuid, uuid, sizeof(TSS_UUID)); + result = TSS_SUCCESS; + break; + } + } + MUTEX_UNLOCK(mem_cache_lock); + + return result; +} |