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

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <inttypes.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"


TSS_RESULT
Tspi_TPM_SetStatus(TSS_HTPM hTPM,	/* in */
		   TSS_FLAG statusFlag,	/* in */
		   TSS_BOOL fTpmState)	/* in */
{
	TPM_AUTH auth, *pAuth;
	TSS_RESULT result;
	TCPA_DIGEST hashDigest;
	TSS_HCONTEXT tspContext;
	TSS_HPOLICY hPolicy;
	TSS_HPOLICY hOperatorPolicy;
	Trspi_HashCtx hashCtx;
	UINT32 tpmVersion;

	if ((result = obj_tpm_get_tsp_context(hTPM, &tspContext)))
		return result;

	if ((result = obj_tpm_get_policy(hTPM, TSS_POLICY_USAGE, &hPolicy)))
		return result;

	switch (statusFlag) {
	case TSS_TPMSTATUS_DISABLEOWNERCLEAR:
		result = Trspi_HashInit(&hashCtx, TSS_HASH_SHA1);
		result |= Trspi_Hash_UINT32(&hashCtx, TPM_ORD_DisableOwnerClear);
		if ((result |= Trspi_HashFinal(&hashCtx, hashDigest.digest)))
			return result;

		if ((result = secret_PerformAuth_OIAP(hTPM, TPM_ORD_DisableOwnerClear, hPolicy,
						      FALSE, &hashDigest, &auth)))
			return result;

		if ((result = TCS_API(tspContext)->DisableOwnerClear(tspContext, &auth)))
			return result;

		result = Trspi_HashInit(&hashCtx, TSS_HASH_SHA1);
		result |= Trspi_Hash_UINT32(&hashCtx, result);
		result |= Trspi_Hash_UINT32(&hashCtx, TPM_ORD_DisableOwnerClear);
		if ((result |= Trspi_HashFinal(&hashCtx, hashDigest.digest)))
			return result;

		if ((result = obj_policy_validate_auth_oiap(hPolicy, &hashDigest, &auth)))
			return result;
		break;
	case TSS_TPMSTATUS_DISABLEFORCECLEAR:
		result = TCS_API(tspContext)->DisableForceClear(tspContext);
		break;
	case TSS_TPMSTATUS_DISABLED:
	case TSS_TPMSTATUS_OWNERSETDISABLE:
		result = Trspi_HashInit(&hashCtx, TSS_HASH_SHA1);
		result |= Trspi_Hash_UINT32(&hashCtx, TPM_ORD_OwnerSetDisable);
		result |= Trspi_Hash_BOOL(&hashCtx, fTpmState);
		if ((result |= Trspi_HashFinal(&hashCtx, hashDigest.digest)))
			return result;

		if ((result = secret_PerformAuth_OIAP(hTPM, TPM_ORD_OwnerSetDisable, hPolicy,
						      FALSE, &hashDigest, &auth)))
			return result;

		if ((result = TCS_API(tspContext)->OwnerSetDisable(tspContext, fTpmState, &auth)))
			return result;

		result = Trspi_HashInit(&hashCtx, TSS_HASH_SHA1);
		result |= Trspi_Hash_UINT32(&hashCtx, result);
		result |= Trspi_Hash_UINT32(&hashCtx, TPM_ORD_OwnerSetDisable);
		if ((result |= Trspi_HashFinal(&hashCtx, hashDigest.digest)))
			return result;

		if ((result = obj_policy_validate_auth_oiap(hPolicy, &hashDigest, &auth)))
			return result;
		break;
	case TSS_TPMSTATUS_PHYSICALDISABLE:
		if (fTpmState)
			result = TCS_API(tspContext)->PhysicalDisable(tspContext);
		else
			result = TCS_API(tspContext)->PhysicalEnable(tspContext);
		break;
	case TSS_TPMSTATUS_DEACTIVATED:
	case TSS_TPMSTATUS_PHYSICALSETDEACTIVATED:
		result = TCS_API(tspContext)->PhysicalSetDeactivated(tspContext, fTpmState);
		break;
	case TSS_TPMSTATUS_SETTEMPDEACTIVATED:
		if ((result = obj_context_get_tpm_version(tspContext, &tpmVersion)))
			return result;

		/* XXX Change 0,1,2 to #defines */
		switch (tpmVersion) {
		case 0:
		case 1:
			result = TCS_API(tspContext)->SetTempDeactivated(tspContext);
			break;
		case 2:
			if ((result = obj_tpm_get_policy(hTPM, TSS_POLICY_OPERATOR, &hOperatorPolicy)))
				return result;

			if (hOperatorPolicy != NULL_HPOLICY) {
				result = Trspi_HashInit(&hashCtx, TSS_HASH_SHA1);
				result |= Trspi_Hash_UINT32(&hashCtx, TPM_ORD_SetTempDeactivated);
				if ((result |= Trspi_HashFinal(&hashCtx, hashDigest.digest)))
					return result;

				pAuth = &auth;
				if ((result = secret_PerformAuth_OIAP(hTPM,
								      TPM_ORD_SetTempDeactivated,
								      hOperatorPolicy, FALSE,
								      &hashDigest, pAuth)))
					return result;
			}
			else
				pAuth = NULL;

			if ((result = TCS_API(tspContext)->SetTempDeactivated2(tspContext, pAuth)))
				return result;

			if (pAuth) {
				result = Trspi_HashInit(&hashCtx, TSS_HASH_SHA1);
				result |= Trspi_Hash_UINT32(&hashCtx, result);
				result |= Trspi_Hash_UINT32(&hashCtx, TPM_ORD_SetTempDeactivated);
				if ((result |= Trspi_HashFinal(&hashCtx, hashDigest.digest)))
					return result;

				if ((result = obj_policy_validate_auth_oiap(hOperatorPolicy,
									    &hashDigest,
									    pAuth)))
					return result;
			}
			break;
		default:
			return TSPERR(TSS_E_INTERNAL_ERROR);
		}
		break;
	case TSS_TPMSTATUS_SETOWNERINSTALL:
		result = TCS_API(tspContext)->SetOwnerInstall(tspContext, fTpmState);
		break;
	case TSS_TPMSTATUS_DISABLEPUBEKREAD:
		result = Trspi_HashInit(&hashCtx, TSS_HASH_SHA1);
		result |= Trspi_Hash_UINT32(&hashCtx, TPM_ORD_DisablePubekRead);
		if ((result |= Trspi_HashFinal(&hashCtx, hashDigest.digest)))
			return result;

		if ((result = secret_PerformAuth_OIAP(hTPM, TPM_ORD_DisablePubekRead, hPolicy,
						      FALSE, &hashDigest, &auth)))
			return result;

		if ((result = TCS_API(tspContext)->DisablePubekRead(tspContext, &auth)))
			return result;

		result = Trspi_HashInit(&hashCtx, TSS_HASH_SHA1);
		result |= Trspi_Hash_UINT32(&hashCtx, result);
		result |= Trspi_Hash_UINT32(&hashCtx, TPM_ORD_DisablePubekRead);
		if ((result |= Trspi_HashFinal(&hashCtx, hashDigest.digest)))
			return result;

		if ((result = obj_policy_validate_auth_oiap(hPolicy, &hashDigest, &auth)))
			return result;
		break;
	case TSS_TPMSTATUS_ALLOWMAINTENANCE:
		/* Allow maintenance cannot be set to TRUE in the TPM */
		if (fTpmState)
			return TSPERR(TSS_E_BAD_PARAMETER);

		/* The path to setting allow maintenance to FALSE is through
		 * KillMaintenanceFeature */
		return Tspi_TPM_KillMaintenanceFeature(hTPM);
		break;
#ifdef TSS_BUILD_TSS12
	case TSS_TPMSTATUS_DISABLEPUBSRKREAD:
		/* The logic of setting a 'disable' flag is reversed in the TPM, where setting this
		 * flag to TRUE will enable the SRK read, while FALSE disables it. So we need to
		 * flip the bool here. Sigh... */
		fTpmState = fTpmState ? FALSE : TRUE;

		result = TSP_SetCapability(tspContext, hTPM, hPolicy, TPM_SET_PERM_FLAGS,
					   TPM_PF_READSRKPUB, fTpmState);
		break;
	case TSS_TPMSTATUS_RESETLOCK:
		/* ignoring the bool here */
		result = Trspi_HashInit(&hashCtx, TSS_HASH_SHA1);
		result |= Trspi_Hash_UINT32(&hashCtx, TPM_ORD_ResetLockValue);
		if ((result |= Trspi_HashFinal(&hashCtx, hashDigest.digest)))
			return result;

		if ((result = secret_PerformAuth_OIAP(hTPM, TPM_ORD_ResetLockValue, hPolicy,
						      FALSE, &hashDigest, &auth)))
			return result;

		if ((result = TCS_API(tspContext)->ResetLockValue(tspContext, &auth)))
			return result;

		result = Trspi_HashInit(&hashCtx, TSS_HASH_SHA1);
		result |= Trspi_Hash_UINT32(&hashCtx, result);
		result |= Trspi_Hash_UINT32(&hashCtx, TPM_ORD_ResetLockValue);
		if ((result |= Trspi_HashFinal(&hashCtx, hashDigest.digest)))
			return result;

		if ((result = obj_policy_validate_auth_oiap(hPolicy, &hashDigest, &auth)))
			return result;
		break;
#endif
#ifndef TSS_SPEC_COMPLIANCE
	case TSS_TPMSTATUS_PHYSPRES_LIFETIMELOCK:
		/* set the lifetime lock bit */
		result = TCS_API(tspContext)->PhysicalPresence(tspContext,
							       TPM_PHYSICAL_PRESENCE_LIFETIME_LOCK);
		break;
	case TSS_TPMSTATUS_PHYSPRES_HWENABLE:
		/* HWENABLE, TRUE -> set the TPM_PHYSICAL_PRESENCE_HW_ENABLE bit
		 * HWENABLE, FALSE -> set the TPM_PHYSICAL_PRESENCE_HW_DISABLE bit */
		if (fTpmState)
			result = TCS_API(tspContext)->PhysicalPresence(tspContext,
							       TPM_PHYSICAL_PRESENCE_HW_ENABLE);
		else
			result = TCS_API(tspContext)->PhysicalPresence(tspContext,
							       TPM_PHYSICAL_PRESENCE_HW_DISABLE);
		break;
	case TSS_TPMSTATUS_PHYSPRES_CMDENABLE:
		/* CMDENABLE, TRUE -> set the TPM_PHYSICAL_PRESENCE_CMD_ENABLE bit
		 * CMDENABLE, FALSE -> set the TPM_PHYSICAL_PRESENCE_CMD_DISABLE bit */
		if (fTpmState)
			result = TCS_API(tspContext)->PhysicalPresence(tspContext,
							       TPM_PHYSICAL_PRESENCE_CMD_ENABLE);
		else
			result = TCS_API(tspContext)->PhysicalPresence(tspContext,
							       TPM_PHYSICAL_PRESENCE_CMD_DISABLE);
		break;
	case TSS_TPMSTATUS_PHYSPRES_LOCK:
		/* set the physical presence lock bit */
		result = TCS_API(tspContext)->PhysicalPresence(tspContext,
							       TPM_PHYSICAL_PRESENCE_LOCK);
		break;
	case TSS_TPMSTATUS_PHYSPRESENCE:
		/* set the physical presence state */
		result = TCS_API(tspContext)->PhysicalPresence(tspContext, (fTpmState ?
							       TPM_PHYSICAL_PRESENCE_PRESENT :
							       TPM_PHYSICAL_PRESENCE_NOTPRESENT));
		break;
#endif
	default:
		return TSPERR(TSS_E_BAD_PARAMETER);
		break;
	}

	return result;
}

TSS_RESULT
Tspi_TPM_GetStatus(TSS_HTPM hTPM,		/* in */
		   TSS_FLAG statusFlag,		/* in */
		   TSS_BOOL * pfTpmState)	/* out */
{
	TSS_HCONTEXT tspContext;
	TSS_RESULT result;
	UINT32 nonVolFlags;
	UINT32 volFlags;

	if (pfTpmState == NULL)
		return TSPERR(TSS_E_BAD_PARAMETER);

	if ((result = obj_tpm_get_tsp_context(hTPM, &tspContext)))
		return result;

	if ((result = get_tpm_flags(tspContext, hTPM, &volFlags, &nonVolFlags)))
		return result;

	switch (statusFlag) {
	case TSS_TPMSTATUS_DISABLEOWNERCLEAR:
		*pfTpmState = BOOL(nonVolFlags & TSS_TPM_PF_DISABLEOWNERCLEAR_BIT);
		break;
	case TSS_TPMSTATUS_DISABLEFORCECLEAR:
		*pfTpmState = BOOL(volFlags & TSS_TPM_SF_DISABLEFORCECLEAR_BIT);
		break;
	case TSS_TPMSTATUS_DISABLED:
	case TSS_TPMSTATUS_OWNERSETDISABLE:
		*pfTpmState = BOOL(nonVolFlags & TSS_TPM_PF_DISABLE_BIT);
		break;
	case TSS_TPMSTATUS_DEACTIVATED:
	case TSS_TPMSTATUS_PHYSICALSETDEACTIVATED:
		*pfTpmState = BOOL(nonVolFlags & TSS_TPM_PF_DEACTIVATED_BIT);
		break;
	case TSS_TPMSTATUS_SETTEMPDEACTIVATED:
		*pfTpmState = BOOL(volFlags & TSS_TPM_SF_DEACTIVATED_BIT);
		break;
	case TSS_TPMSTATUS_SETOWNERINSTALL:
		*pfTpmState = BOOL(nonVolFlags & TSS_TPM_PF_OWNERSHIP_BIT);
		break;
	case TSS_TPMSTATUS_DISABLEPUBEKREAD:
		*pfTpmState = INVBOOL(nonVolFlags & TSS_TPM_PF_READPUBEK_BIT);
		break;
	case TSS_TPMSTATUS_ALLOWMAINTENANCE:
		*pfTpmState = BOOL(nonVolFlags & TSS_TPM_PF_ALLOWMAINTENANCE_BIT);
		break;
	case TSS_TPMSTATUS_MAINTENANCEUSED:
		*pfTpmState = BOOL(nonVolFlags & TSS_TPM_PF_MAINTENANCEDONE_BIT);
		break;
	case TSS_TPMSTATUS_PHYSPRES_LIFETIMELOCK:
		*pfTpmState = BOOL(nonVolFlags & TSS_TPM_PF_PHYSICALPRESENCELIFETIMELOCK_BIT);
		break;
	case TSS_TPMSTATUS_PHYSPRES_HWENABLE:
		*pfTpmState = BOOL(nonVolFlags & TSS_TPM_PF_PHYSICALPRESENCEHWENABLE_BIT);
		break;
	case TSS_TPMSTATUS_PHYSPRES_CMDENABLE:
		*pfTpmState = BOOL(nonVolFlags & TSS_TPM_PF_PHYSICALPRESENCECMDENABLE_BIT);
		break;
	case TSS_TPMSTATUS_CEKP_USED:
		*pfTpmState = BOOL(nonVolFlags & TSS_TPM_PF_CEKPUSED_BIT);
		break;
	case TSS_TPMSTATUS_PHYSPRESENCE:
		*pfTpmState = BOOL(volFlags & TSS_TPM_SF_PHYSICALPRESENCE_BIT);
		break;
	case TSS_TPMSTATUS_PHYSPRES_LOCK:
		*pfTpmState = BOOL(volFlags & TSS_TPM_SF_PHYSICALPRESENCELOCK_BIT);
		break;
	case TSS_TPMSTATUS_TPMPOST:
		*pfTpmState = BOOL(nonVolFlags & TSS_TPM_PF_TPMPOST_BIT);
		break;
	case TSS_TPMSTATUS_TPMPOSTLOCK:
		*pfTpmState = BOOL(nonVolFlags & TSS_TPM_PF_TPMPOSTLOCK_BIT);
		break;
	case TSS_TPMSTATUS_FIPS:
		*pfTpmState = BOOL(nonVolFlags & TSS_TPM_PF_FIPS_BIT);
		break;
	case TSS_TPMSTATUS_ENABLE_REVOKEEK:
		*pfTpmState = BOOL(nonVolFlags & TSS_TPM_PF_ENABLEREVOKEEK_BIT);
		break;
	case TSS_TPMSTATUS_TPM_ESTABLISHED:
		*pfTpmState = BOOL(nonVolFlags & TSS_TPM_PF_RESETESTABLISHMENTBIT_BIT);
		break;
	case TSS_TPMSTATUS_NV_LOCK:
		*pfTpmState = BOOL(nonVolFlags & TSS_TPM_PF_NV_LOCKED_BIT);
		break;
	case TSS_TPMSTATUS_POSTINITIALISE:
		/* There is no way to query the TPM for this flag. */
		result = TSPERR(TSS_E_NOTIMPL);
		break;
#ifdef TSS_BUILD_TSS12
	case TSS_TPMSTATUS_DISABLEPUBSRKREAD:
		*pfTpmState = INVBOOL(nonVolFlags & TSS_TPM_PF_READSRKPUB_BIT);
		break;
	case TSS_TPMSTATUS_OPERATORINSTALLED:
		*pfTpmState = BOOL(nonVolFlags & TSS_TPM_PF_OPERATOR_BIT);
		break;
#endif
	default:
		return TSPERR(TSS_E_BAD_PARAMETER);
		break;
	}

	return TSS_SUCCESS;
}
