/*
* Licensed Materials - Property of IBM
*
* trousers - An open source TCG Software Stack
*
* (C) Copyright International Business Machines Corp. 2004
*
*/
#include <time.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "trousers/tss.h"
#include "trousers_types.h"
#include "tcs_context.h"
#include "tcs_tsp.h"
#include "tcs_utils.h"
#include "tcs_int_literals.h"
#include "capabilities.h"
#include "tcslog.h"
#include "tcsd_wrap.h"
#include "tcsd.h"
unsigned long nextContextHandle = 0xA0000000;
struct tcs_context *tcs_context_table = NULL;
MUTEX_DECLARE_INIT(tcs_ctx_lock);
TCS_CONTEXT_HANDLE getNextHandle();
struct tcs_context *create_tcs_context();
struct tcs_context *get_context(TCS_CONTEXT_HANDLE);
struct tcs_context *get_previous_context(TCS_CONTEXT_HANDLE);
TSS_BOOL initContextHandle = 1;
TCS_CONTEXT_HANDLE
getNextHandle()
{
UINT32 tempRand;
time_t currentTime;
if (initContextHandle) {
currentTime = time(NULL);
srand(currentTime);
tempRand = rand();
tempRand = tempRand << 16;
tempRand &= 0x00FF0000;
nextContextHandle |= tempRand;
initContextHandle = 0;
}
currentTime = time(NULL);
srand(currentTime + 1);
tempRand = rand();
tempRand = tempRand << 8;
tempRand &= 0x0000FF00;
if (nextContextHandle == 0)
return getNextHandle();
else
return ((nextContextHandle++) | tempRand);
}
struct tcs_context *
create_tcs_context()
{
struct tcs_context *ret = (struct tcs_context *)calloc(1, sizeof(struct tcs_context));
if (ret != NULL) {
ret->handle = getNextHandle();
COND_INIT(ret->cond);
}
return ret;
}
struct tcs_context *
get_context(TCS_CONTEXT_HANDLE handle)
{
struct tcs_context *index;
index = tcs_context_table;
while (index) {
if (index->handle == handle)
break;
index = index->next;
}
return index;
}
struct tcs_context *
get_previous_context(TCS_CONTEXT_HANDLE handle)
{
struct tcs_context *index;
index = tcs_context_table;
while (index) {
if (index->next) {
if (index->next->handle == handle)
return index;
}
index = index->next;
}
return 0;
}
void
destroy_context(TCS_CONTEXT_HANDLE handle)
{
struct tcs_context *toKill;
struct tcs_context *previous;
MUTEX_LOCK(tcs_ctx_lock);
toKill = get_context(handle);
previous = get_previous_context(handle);
if (!previous && tcs_context_table->handle == handle) {
/* this means that toKill is the first one */
tcs_context_table = tcs_context_table->next;
} else if (previous && toKill) {
/* both are found */
previous->next = toKill->next;
} else {
MUTEX_UNLOCK(tcs_ctx_lock);
return;
}
MUTEX_UNLOCK(tcs_ctx_lock);
CTX_ref_count_keys(toKill);
#ifdef TSS_BUILD_TRANSPORT
/* Free existing transport session if necessary */
if (toKill != NULL && toKill->transHandle)
TCSP_FlushSpecific_Common(toKill->transHandle, TPM_RT_TRANS);
#endif
free(toKill);
}
TCS_CONTEXT_HANDLE
make_context()
{
struct tcs_context *index;
MUTEX_LOCK(tcs_ctx_lock);
index = tcs_context_table;
if (!index) {
tcs_context_table = create_tcs_context();
if (tcs_context_table == NULL) {
LogError("Malloc Failure.");
MUTEX_UNLOCK(tcs_ctx_lock);
return 0;
}
index = tcs_context_table;
} else {
while (index->next) {
index = index->next;
}
index->next = create_tcs_context();
if (index->next == NULL) {
LogError("Malloc Failure.");
MUTEX_UNLOCK(tcs_ctx_lock);
return 0;
}
index = index->next;
}
MUTEX_UNLOCK(tcs_ctx_lock);
return index->handle;
}
TSS_RESULT
ctx_verify_context(TCS_CONTEXT_HANDLE tcsContext)
{
struct tcs_context *c;
MUTEX_LOCK(tcs_ctx_lock);
c = get_context(tcsContext);
MUTEX_UNLOCK(tcs_ctx_lock);
if (c == NULL) {
LogDebug("Fail: Context %x not found", tcsContext);
return TCSERR(TCS_E_INVALID_CONTEXTHANDLE);
}
return TSS_SUCCESS;
}
COND_VAR *
ctx_get_cond_var(TCS_CONTEXT_HANDLE tcs_handle)
{
struct tcs_context *c;
COND_VAR *ret = NULL;
MUTEX_LOCK(tcs_ctx_lock);
c = get_context(tcs_handle);
if (c != NULL)
ret = &c->cond;
MUTEX_UNLOCK(tcs_ctx_lock);
return ret;
}
/* the only transport flag at the TCS level is whether the session is exclusive or not. If the app
* is requesting an exclusive transport session, check that no other exclusive sessions exist and
* if not, flag this context as being the one. If so, return internal error. */
TSS_RESULT
ctx_req_exclusive_transport(TCS_CONTEXT_HANDLE tcsContext)
{
TSS_RESULT result = TSS_SUCCESS;
struct tcs_context *tmp, *self = NULL;
/* If the daemon is configured to ignore apps that want an exclusive transport, just
* return */
if (!tcsd_options.exclusive_transport)
return result;
MUTEX_LOCK(tcs_ctx_lock);
tmp = tcs_context_table;
while (tmp) {
if (tmp->flags & TSS_CONTEXT_FLAG_TRANSPORT_EXCLUSIVE) {
result = TCSERR(TSS_E_INTERNAL_ERROR);
goto done;
}
if (tmp->handle == tcsContext)
self = tmp;
tmp = tmp->next;
}
if (self)
self->flags |= TSS_CONTEXT_FLAG_TRANSPORT_EXCLUSIVE;
else
result = TCSERR(TCS_E_INVALID_CONTEXTHANDLE);
done:
MUTEX_UNLOCK(tcs_ctx_lock);
return result;
}
TSS_RESULT
ctx_set_transport_enabled(TCS_CONTEXT_HANDLE tcsContext, UINT32 hTransHandle)
{
TSS_RESULT result = TSS_SUCCESS;
struct tcs_context *tmp, *self = NULL;
MUTEX_LOCK(tcs_ctx_lock);
tmp = tcs_context_table;
while (tmp) {
if (tmp->handle == tcsContext) {
self = tmp;
break;
}
tmp = tmp->next;
}
if (self) {
self->flags |= TSS_CONTEXT_FLAG_TRANSPORT_ENABLED;
self->transHandle = hTransHandle;
} else
result = TCSERR(TCS_E_INVALID_CONTEXTHANDLE);
MUTEX_UNLOCK(tcs_ctx_lock);
return result;
}
TSS_RESULT
ctx_set_transport_disabled(TCS_CONTEXT_HANDLE tcsContext, TCS_HANDLE *transHandle)
{
TSS_RESULT result = TSS_SUCCESS;
struct tcs_context *tmp, *self = NULL;
MUTEX_LOCK(tcs_ctx_lock);
tmp = tcs_context_table;
while (tmp) {
if (tmp->handle == tcsContext) {
self = tmp;
break;
}
tmp = tmp->next;
}
if (self) {
if (!transHandle || *transHandle == self->transHandle) {
self->transHandle = 0;
self->flags &= ~TSS_CONTEXT_FLAG_TRANSPORT_ENABLED;
}
} else
result = TCSERR(TCS_E_INVALID_CONTEXTHANDLE);
MUTEX_UNLOCK(tcs_ctx_lock);
return result;
}