
/*
 * 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 <syslog.h>
#include <string.h>
#include <netdb.h>

#include "trousers/tss.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 "tcsd_wrap.h"
#include "tcsd.h"
#include "tcs_utils.h"
#include "rpc_tcstp_tcs.h"


TSS_RESULT
tcs_wrap_GetPcrEvent(struct tcsd_thread_data *data)
{
	TCS_CONTEXT_HANDLE hContext;
	TSS_PCR_EVENT *pEvent = NULL;
	TSS_RESULT result;
	UINT32 pcrIndex, number;
	BYTE lengthOnly;

	if (getData(TCSD_PACKET_TYPE_UINT32, 0, &hContext, 0, &data->comm))
		return TCSERR(TSS_E_INTERNAL_ERROR);

	if ((result = ctx_verify_context(hContext)))
		goto done;

	LogDebugFn("thread %ld context %x", THREAD_ID, hContext);

	if (getData(TCSD_PACKET_TYPE_UINT32, 1, &pcrIndex, 0, &data->comm))
		return TCSERR(TSS_E_INTERNAL_ERROR);

	if (getData(TCSD_PACKET_TYPE_UINT32, 2, &number, 0, &data->comm))
		return TCSERR(TSS_E_INTERNAL_ERROR);

	if (getData(TCSD_PACKET_TYPE_BYTE, 3, &lengthOnly, 0, &data->comm))
		return TCSERR(TSS_E_INTERNAL_ERROR);

	if (lengthOnly)
		result = TCS_GetPcrEvent_Internal(hContext, pcrIndex, &number, NULL);
	else
		result = TCS_GetPcrEvent_Internal(hContext, pcrIndex, &number, &pEvent);

	if (result == TSS_SUCCESS) {
		initData(&data->comm, 2);
		if (setData(TCSD_PACKET_TYPE_UINT32, 0, &number, 0, &data->comm)) {
			if (lengthOnly == FALSE)
				free_external_events(1, pEvent);
			free(pEvent);
			return TCSERR(TSS_E_INTERNAL_ERROR);
		}

		if (lengthOnly == FALSE) {
			if (setData(TCSD_PACKET_TYPE_PCR_EVENT, 1, pEvent, 0, &data->comm)) {
				free_external_events(1, pEvent);
				free(pEvent);
				return TCSERR(TSS_E_INTERNAL_ERROR);
			}
			free_external_events(1, pEvent);
			free(pEvent);
		}
	} else
done:		initData(&data->comm, 0);

	data->comm.hdr.u.result = result;

	return TSS_SUCCESS;
}


TSS_RESULT
tcs_wrap_GetPcrEventsByPcr(struct tcsd_thread_data *data)
{
	TCS_CONTEXT_HANDLE hContext;
	TSS_PCR_EVENT *ppEvents = NULL;
	TSS_RESULT result;
	UINT32 firstEvent, eventCount, totalSize, pcrIndex, i, j;

	if (getData(TCSD_PACKET_TYPE_UINT32, 0, &hContext, 0, &data->comm))
		return TCSERR(TSS_E_INTERNAL_ERROR);

	if ((result = ctx_verify_context(hContext)))
		goto done;

	LogDebugFn("thread %ld context %x", THREAD_ID, hContext);

	if (getData(TCSD_PACKET_TYPE_UINT32, 1, &pcrIndex, 0, &data->comm))
		return TCSERR(TSS_E_INTERNAL_ERROR);

	if (getData(TCSD_PACKET_TYPE_UINT32, 2, &firstEvent, 0, &data->comm))
		return TCSERR(TSS_E_INTERNAL_ERROR);

	if (getData(TCSD_PACKET_TYPE_UINT32, 3, &eventCount, 0, &data->comm))
		return TCSERR(TSS_E_INTERNAL_ERROR);

	result = TCS_GetPcrEventsByPcr_Internal(hContext, pcrIndex, firstEvent, &eventCount, &ppEvents);

	if (result == TSS_SUCCESS) {
		/* XXX totalSize not used */
		for (i = 0, totalSize = 0; i < eventCount; i++)
			totalSize += get_pcr_event_size(&(ppEvents[i]));

		initData(&data->comm, eventCount + 1);
		if (setData(TCSD_PACKET_TYPE_UINT32, 0, &eventCount, 0, &data->comm)) {
			free_external_events(eventCount, ppEvents);
			free(ppEvents);
			return TCSERR(TSS_E_INTERNAL_ERROR);
		}

		i = 1;
		for (j = 0; j < eventCount; j++) {
			if (setData(TCSD_PACKET_TYPE_PCR_EVENT, i++, &(ppEvents[j]), 0, &data->comm)) {
				free_external_events(eventCount, ppEvents);
				free(ppEvents);
				return TCSERR(TSS_E_INTERNAL_ERROR);
			}
		}

		free_external_events(eventCount, ppEvents);
		free(ppEvents);
	} else
done:		initData(&data->comm, 0);

	data->comm.hdr.u.result = result;

	return TSS_SUCCESS;
}

TSS_RESULT
tcs_wrap_GetPcrEventLog(struct tcsd_thread_data *data)
{
	TCS_CONTEXT_HANDLE hContext;
	TSS_PCR_EVENT *ppEvents;
	TSS_RESULT result;
	UINT32 eventCount, totalSize, i, j;

	if (getData(TCSD_PACKET_TYPE_UINT32, 0, &hContext, 0, &data->comm))
		return TCSERR(TSS_E_INTERNAL_ERROR);

	LogDebugFn("thread %ld context %x", THREAD_ID, hContext);

	result = TCS_GetPcrEventLog_Internal(hContext, &eventCount, &ppEvents);

	if (result == TSS_SUCCESS) {
		for (i = 0, totalSize = 0; i < eventCount; i++)
			totalSize += get_pcr_event_size(&(ppEvents[i]));

		initData(&data->comm, eventCount + 1);
		if (setData(TCSD_PACKET_TYPE_UINT32, 0, &eventCount, 0, &data->comm)) {
			free_external_events(eventCount, ppEvents);
			free(ppEvents);
			return TCSERR(TSS_E_INTERNAL_ERROR);
		}

		i = 1;
		for (j = 0; j < eventCount; j++) {
			if (setData(TCSD_PACKET_TYPE_PCR_EVENT, i++, &(ppEvents[j]), 0, &data->comm)) {
				free_external_events(eventCount, ppEvents);
				free(ppEvents);
				return TCSERR(TSS_E_INTERNAL_ERROR);
			}
		}

		free_external_events(eventCount, ppEvents);
		free(ppEvents);
	} else
		initData(&data->comm, 0);

	data->comm.hdr.u.result = result;

	return TSS_SUCCESS;
}

TSS_RESULT
tcs_wrap_LogPcrEvent(struct tcsd_thread_data *data)
{
	TCS_CONTEXT_HANDLE hContext;
	TSS_PCR_EVENT event;
	TSS_RESULT result;
	UINT32 number;

	/* Receive */
	if (getData(TCSD_PACKET_TYPE_UINT32, 0, &hContext, 0, &data->comm))
		return TCSERR(TSS_E_INTERNAL_ERROR);

	if ((result = ctx_verify_context(hContext)))
		goto done;

	LogDebugFn("thread %ld context %x", THREAD_ID, hContext);

	if (getData(TCSD_PACKET_TYPE_PCR_EVENT , 1, &event, 0, &data->comm))
		return TCSERR(TSS_E_INTERNAL_ERROR);

	result = TCS_LogPcrEvent_Internal(hContext, event, &number);

	if (result == TSS_SUCCESS) {
		initData(&data->comm, 1);
		if (setData(TCSD_PACKET_TYPE_UINT32, 0, &number, 0, &data->comm)) {
			return TCSERR(TSS_E_INTERNAL_ERROR);
		}
	} else
done:		initData(&data->comm, 0);

	data->comm.hdr.u.result = result;

	return TSS_SUCCESS;
}

void
LoadBlob_PCR_EVENT(UINT64 *offset, BYTE *blob, TSS_PCR_EVENT *event)
{
	LoadBlob_VERSION(offset, blob, (TPM_VERSION *)&(event->versionInfo));
	LoadBlob_UINT32(offset, event->ulPcrIndex, blob);
	LoadBlob_UINT32(offset, event->eventType, blob);

	LoadBlob_UINT32(offset, event->ulPcrValueLength, blob);
	if (event->ulPcrValueLength > 0)
		LoadBlob(offset, event->ulPcrValueLength, blob, event->rgbPcrValue);

	LoadBlob_UINT32(offset, event->ulEventLength, blob);
	if (event->ulEventLength > 0)
		LoadBlob(offset, event->ulEventLength, blob, event->rgbEvent);

}

TSS_RESULT
UnloadBlob_PCR_EVENT(UINT64 *offset, BYTE *blob, TSS_PCR_EVENT *event)
{
	if (!event) {
		UINT32 ulPcrValueLength, ulEventLength;

		UnloadBlob_VERSION(offset, blob, NULL);
		UnloadBlob_UINT32(offset, NULL, blob);
		UnloadBlob_UINT32(offset, NULL, blob);

		UnloadBlob_UINT32(offset, &ulPcrValueLength, blob);
		(*offset) += ulPcrValueLength;

		UnloadBlob_UINT32(offset, &ulEventLength, blob);
		(*offset) += ulEventLength;

		return TSS_SUCCESS;
	}

	UnloadBlob_VERSION(offset, blob, (TPM_VERSION *)&(event->versionInfo));
	UnloadBlob_UINT32(offset, &event->ulPcrIndex, blob);
	UnloadBlob_UINT32(offset, &event->eventType, blob);

	UnloadBlob_UINT32(offset, &event->ulPcrValueLength, blob);
	if (event->ulPcrValueLength > 0) {
		event->rgbPcrValue = malloc(event->ulPcrValueLength);
		if (event->rgbPcrValue == NULL) {
			LogError("malloc of %u bytes failed.", event->ulPcrValueLength);
			return TCSERR(TSS_E_OUTOFMEMORY);
		}

		UnloadBlob(offset, event->ulPcrValueLength, blob, event->rgbPcrValue);
	} else {
		event->rgbPcrValue = NULL;
	}

	UnloadBlob_UINT32(offset, &event->ulEventLength, blob);
	if (event->ulEventLength > 0) {
		event->rgbEvent = malloc(event->ulEventLength);
		if (event->rgbEvent == NULL) {
			LogError("malloc of %u bytes failed.", event->ulEventLength);
			free(event->rgbPcrValue);
			return TCSERR(TSS_E_OUTOFMEMORY);
		}

		UnloadBlob(offset, event->ulEventLength, blob, event->rgbEvent);
	} else {
		event->rgbEvent = NULL;
	}

	return TSS_SUCCESS;
}
