/* * Licensed Materials - Property of IBM * * trousers - An open source TCG Software Stack * * (C) Copyright International Business Machines Corp. 2004-2006 * */ #include #include #include #include #include #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) { result = TSS_SUCCESS; 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; }