
/*
 * Licensed Materials - Property of IBM
 *
 * trousers - An open source TCG Software Stack
 *
 * (C) Copyright International Business Machines Corp. 2007
 *
 */


#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>

#include "trousers/tss.h"
#include "trousers/trousers.h"
#include "trousers_types.h"
#include "spi_utils.h"
#include "capabilities.h"
#include "tsplog.h"
#include "obj.h"
#include "tsp_delegate.h"

void
delfamily_free(void *data)
{
	struct tr_delfamily_obj *delfamily = (struct tr_delfamily_obj *)data;

	free(delfamily);
}

TSS_BOOL
obj_is_delfamily(TSS_HOBJECT hObject)
{
	TSS_BOOL answer = FALSE;

	if ((obj_list_get_obj(&delfamily_list, hObject))) {
		answer = TRUE;
		obj_list_put(&delfamily_list);
	}

	return answer;
}

TSS_RESULT
obj_delfamily_add(TSS_HCONTEXT hContext, TSS_HOBJECT *phObject)
{
	TSS_RESULT result;
	struct tr_delfamily_obj *delfamily = calloc(1, sizeof(struct tr_delfamily_obj));

	if (delfamily == NULL) {
		LogError("malloc of %zd bytes failed.",
				sizeof(struct tr_delfamily_obj));
		return TSPERR(TSS_E_OUTOFMEMORY);
	}

	if ((result = obj_list_add(&delfamily_list, hContext, 0, delfamily, phObject))) {
		free(delfamily);
		return result;
	}

	return TSS_SUCCESS;
}

TSS_RESULT
obj_delfamily_remove(TSS_HDELFAMILY hFamily, TSS_HOBJECT hObject)
{
	TSS_HCONTEXT hContext;
	TSS_RESULT result;

	if (obj_is_tpm(hObject)) {
		if ((result = obj_tpm_get_tsp_context((TSS_HTPM)hObject, &hContext)))
			return result;
	} else
		hContext = (TSS_HCONTEXT)hObject;

	if ((result = obj_list_remove(&delfamily_list, &delfamily_free, hFamily, hContext)))
		return result;

	return TSS_SUCCESS;
}

void
obj_delfamily_find_by_familyid(TSS_HOBJECT hObject, UINT32 familyID, TSS_HDELFAMILY *hFamily)
{
	TSS_HCONTEXT hContext;
	struct tsp_object *obj;
	struct obj_list *list = &delfamily_list;
	struct tr_delfamily_obj *delfamily;

	pthread_mutex_lock(&list->lock);

	*hFamily = NULL_HDELFAMILY;

	if (obj_is_tpm(hObject)) {
		if (obj_tpm_get_tsp_context((TSS_HTPM)hObject, &hContext)) {
			pthread_mutex_unlock(&list->lock);
			return;
		}
	} else
		hContext = (TSS_HCONTEXT)hObject;

	for (obj = list->head; obj; obj = obj->next) {
		if (obj->tspContext != hContext)
			continue;

		delfamily = (struct tr_delfamily_obj *)obj->data;
		if (delfamily->familyID == familyID) {
			*hFamily = obj->handle;
			break;
		}
	}

	pthread_mutex_unlock(&list->lock);
}

TSS_RESULT
obj_delfamily_get_tsp_context(TSS_HDELFAMILY hFamily, TSS_HCONTEXT *hContext)
{
	struct tsp_object *obj;

	if ((obj = obj_list_get_obj(&delfamily_list, hFamily)) == NULL)
		return TSPERR(TSS_E_INVALID_HANDLE);

	*hContext = obj->tspContext;

	obj_list_put(&delfamily_list);

	return TSS_SUCCESS;
}

TSS_RESULT
obj_delfamily_set_locked(TSS_HDELFAMILY hFamily, TSS_BOOL state, TSS_BOOL setInTpm)
{
	struct tsp_object *obj;
	struct tr_delfamily_obj *delfamily;
	TSS_HTPM hTpm;
	UINT32 opDataSize;
	BYTE opData[8];
	UINT32 outDataSize;
	BYTE *outData = NULL;
	UINT64 offset;
	TSS_RESULT result = TSS_SUCCESS;

	if ((obj = obj_list_get_obj(&delfamily_list, hFamily)) == NULL)
		return TSPERR(TSS_E_INVALID_HANDLE);

	delfamily = (struct tr_delfamily_obj *)obj->data;

	if (setInTpm) {
		if ((result = obj_tpm_get(obj->tspContext, &hTpm)))
			goto done;

		offset = 0;
		Trspi_LoadBlob_BOOL(&offset, state, opData);
		opDataSize = offset;
		if ((result = do_delegate_manage(hTpm, delfamily->familyID, TPM_FAMILY_ADMIN,
				opDataSize, opData, &outDataSize, &outData)))
			goto done;
	}

	if (state)
		delfamily->stateFlags |= TSS_DELFAMILY_FLAGS_STATE_LOCKED;
	else
		delfamily->stateFlags &= ~TSS_DELFAMILY_FLAGS_STATE_LOCKED;

done:
	obj_list_put(&delfamily_list);

	free(outData);

	return result;
}

TSS_RESULT
obj_delfamily_get_locked(TSS_HDELFAMILY hFamily, TSS_BOOL *state)
{
	struct tsp_object *obj;
	struct tr_delfamily_obj *delfamily;

	if ((obj = obj_list_get_obj(&delfamily_list, hFamily)) == NULL)
		return TSPERR(TSS_E_INVALID_HANDLE);

	delfamily = (struct tr_delfamily_obj *)obj->data;

	*state = (delfamily->stateFlags & TSS_DELFAMILY_FLAGS_STATE_LOCKED) ? TRUE : FALSE;

	obj_list_put(&delfamily_list);

	return TSS_SUCCESS;
}

TSS_RESULT
obj_delfamily_set_enabled(TSS_HDELFAMILY hFamily, TSS_BOOL state, TSS_BOOL setInTpm)
{
	struct tsp_object *obj;
	struct tr_delfamily_obj *delfamily;
	TSS_HTPM hTpm;
	UINT32 opDataSize;
	BYTE opData[8];
	UINT32 outDataSize;
	BYTE *outData = NULL;
	UINT64 offset;
	TSS_RESULT result = TSS_SUCCESS;

	if ((obj = obj_list_get_obj(&delfamily_list, hFamily)) == NULL)
		return TSPERR(TSS_E_INVALID_HANDLE);

	delfamily = (struct tr_delfamily_obj *)obj->data;

	if (setInTpm) {
		if ((result = obj_tpm_get(obj->tspContext, &hTpm)))
			goto done;

		offset = 0;
		Trspi_LoadBlob_BOOL(&offset, state, opData);
		opDataSize = offset;
		if ((result = do_delegate_manage(hTpm, delfamily->familyID, TPM_FAMILY_ENABLE,
				opDataSize, opData, &outDataSize, &outData)))
			goto done;
	}

	if (state)
		delfamily->stateFlags |= TSS_DELFAMILY_FLAGS_STATE_ENABLED;
	else
		delfamily->stateFlags &= ~TSS_DELFAMILY_FLAGS_STATE_ENABLED;

done:
	obj_list_put(&delfamily_list);

	free(outData);

	return result;
}

TSS_RESULT
obj_delfamily_get_enabled(TSS_HDELFAMILY hFamily, TSS_BOOL *state)
{
	struct tsp_object *obj;
	struct tr_delfamily_obj *delfamily;

	if ((obj = obj_list_get_obj(&delfamily_list, hFamily)) == NULL)
		return TSPERR(TSS_E_INVALID_HANDLE);

	delfamily = (struct tr_delfamily_obj *)obj->data;

	*state = (delfamily->stateFlags & TSS_DELFAMILY_FLAGS_STATE_ENABLED) ? TRUE : FALSE;

	obj_list_put(&delfamily_list);

	return TSS_SUCCESS;
}

TSS_RESULT
obj_delfamily_set_vercount(TSS_HDELFAMILY hFamily, UINT32 verCount)
{
	struct tsp_object *obj;
	struct tr_delfamily_obj *delfamily;

	if ((obj = obj_list_get_obj(&delfamily_list, hFamily)) == NULL)
		return TSPERR(TSS_E_INVALID_HANDLE);

	delfamily = (struct tr_delfamily_obj *)obj->data;

	delfamily->verCount = verCount;

	obj_list_put(&delfamily_list);

	return TSS_SUCCESS;
}

TSS_RESULT
obj_delfamily_get_vercount(TSS_HDELFAMILY hFamily, UINT32 *verCount)
{
	struct tsp_object *obj;
	struct tr_delfamily_obj *delfamily;

	if ((obj = obj_list_get_obj(&delfamily_list, hFamily)) == NULL)
		return TSPERR(TSS_E_INVALID_HANDLE);

	delfamily = (struct tr_delfamily_obj *)obj->data;

	*verCount = delfamily->verCount;

	obj_list_put(&delfamily_list);

	return TSS_SUCCESS;
}

TSS_RESULT
obj_delfamily_set_familyid(TSS_HDELFAMILY hFamily, UINT32 familyID)
{
	struct tsp_object *obj;
	struct tr_delfamily_obj *delfamily;

	if ((obj = obj_list_get_obj(&delfamily_list, hFamily)) == NULL)
		return TSPERR(TSS_E_INVALID_HANDLE);

	delfamily = (struct tr_delfamily_obj *)obj->data;

	delfamily->familyID = familyID;

	obj_list_put(&delfamily_list);

	return TSS_SUCCESS;
}

TSS_RESULT
obj_delfamily_get_familyid(TSS_HDELFAMILY hFamily, UINT32 *familyID)
{
	struct tsp_object *obj;
	struct tr_delfamily_obj *delfamily;

	if ((obj = obj_list_get_obj(&delfamily_list, hFamily)) == NULL)
		return TSPERR(TSS_E_INVALID_HANDLE);

	delfamily = (struct tr_delfamily_obj *)obj->data;

	*familyID = delfamily->familyID;

	obj_list_put(&delfamily_list);

	return TSS_SUCCESS;
}

TSS_RESULT
obj_delfamily_set_label(TSS_HDELFAMILY hFamily, BYTE label)
{
	struct tsp_object *obj;
	struct tr_delfamily_obj *delfamily;

	if ((obj = obj_list_get_obj(&delfamily_list, hFamily)) == NULL)
		return TSPERR(TSS_E_INVALID_HANDLE);

	delfamily = (struct tr_delfamily_obj *)obj->data;

	delfamily->label = label;

	obj_list_put(&delfamily_list);

	return TSS_SUCCESS;
}

TSS_RESULT
obj_delfamily_get_label(TSS_HDELFAMILY hFamily, BYTE *label)
{
	struct tsp_object *obj;
	struct tr_delfamily_obj *delfamily;

	if ((obj = obj_list_get_obj(&delfamily_list, hFamily)) == NULL)
		return TSPERR(TSS_E_INVALID_HANDLE);

	delfamily = (struct tr_delfamily_obj *)obj->data;

	*label = delfamily->label;

	obj_list_put(&delfamily_list);

	return TSS_SUCCESS;
}

