blob: 8da0b3a58ccbae99aad7352c6c3ecaa843fe3132 [file] [log] [blame]
/* Software-based Trusted Platform Module (TPM) Emulator
* Copyright (C) 2004-2010 Mario Strasser <mast@gmx.net>
*
* This module is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published
* by the Free Software Foundation; either version 2 of the License,
* or (at your option) any later version.
*
* This module is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* $Id: tddl.c 364 2010-02-11 10:24:45Z mast $
*/
#include <unistd.h>
#include <string.h>
#include <config.h>
#include "tddl.h"
/* device and socket names */
static const char *tpm_device_name = TPM_DEVICE_NAME;
static const char *tpmd_socket_name = TPM_SOCKET_NAME;
/* TPM device handle */
static int tddli_dh = -1;
/* status of the TPM device driver and the TPM itself */
static TSS_RESULT tddli_driver_status = TDDL_DRIVER_FAILED;
static TSS_RESULT tddli_device_status = TDDL_DEVICE_NOT_FOUND;
#if defined(_WIN32) || defined(_WIN64)
#include "tddl_windows.h"
#else
#include "tddl_unix.h"
#endif
TSS_RESULT Tddli_Open()
{
TSS_RESULT res;
tddli_mutex_lock(&tddli_lock);
if (tddli_dh != -1) {
res = TDDL_E_ALREADY_OPENED;
} else {
res = open_socket(tpmd_socket_name);
if (res != TDDL_SUCCESS) {
res = open_device(tpm_device_name);
}
}
tddli_mutex_unlock(&tddli_lock);
return res;
}
TSS_RESULT Tddli_Close()
{
TSS_RESULT res = TDDL_SUCCESS;
tddli_mutex_lock(&tddli_lock);
if (tddli_dh >= 0) {
close(tddli_dh);
tddli_dh = -1;
} else {
res = TDDL_E_ALREADY_CLOSED;
}
tddli_mutex_unlock(&tddli_lock);
return res;
}
TSS_RESULT Tddli_Cancel()
{
/* this is not supported by the TPM emulator */
return TDDL_E_NOTIMPL;
}
static TSS_RESULT send_to_tpm(BYTE* pTransmitBuf, UINT32 TransmitBufLen)
{
ssize_t res;
res = write(tddli_dh, pTransmitBuf, TransmitBufLen);
if (res < 0 || (UINT32)res != TransmitBufLen) return TDDL_E_IOERROR;
return TDDL_SUCCESS;
}
static TSS_RESULT receive_from_tpm(BYTE* pReceiveBuf, UINT32* puntReceiveBufLen)
{
ssize_t res;
uint32_t len;
if (*puntReceiveBufLen < 10) return TDDL_E_INSUFFICIENT_BUFFER;
res = read(tddli_dh, pReceiveBuf, *puntReceiveBufLen);
if (res < 10) return TDDL_E_IOERROR;
*puntReceiveBufLen = res;
len = ((uint32_t)pReceiveBuf[2] << 24) | ((uint32_t)pReceiveBuf[3] << 16)
| ((uint32_t)pReceiveBuf[4] << 8) | (uint32_t)pReceiveBuf[5];
if (len != *puntReceiveBufLen) return TDDL_E_INSUFFICIENT_BUFFER;
return TDDL_SUCCESS;
}
TSS_RESULT Tddli_TransmitData(BYTE* pTransmitBuf, UINT32 TransmitBufLen,
BYTE* pReceiveBuf, UINT32* puntReceiveBufLen)
{
TSS_RESULT res;
tddli_mutex_lock(&tddli_lock);
if (tddli_dh >= 0) {
res = send_to_tpm(pTransmitBuf, TransmitBufLen);
if (res == TDDL_SUCCESS)
res = receive_from_tpm(pReceiveBuf, puntReceiveBufLen);
} else {
res = TDDL_E_FAIL;
}
tddli_mutex_unlock(&tddli_lock);
return res;
}
static TSS_RESULT cap_version(UINT32 SubCap, BYTE* pCapBuf,
UINT32* puntCapBufLen)
{
TSS_RESULT res;
UINT32 len = 18;
BYTE buf[18];
switch (SubCap) {
case TDDL_CAP_VER_DRV:
if (*puntCapBufLen < 4) return TDDL_E_INSUFFICIENT_BUFFER;
*puntCapBufLen = 4;
memcpy(pCapBuf, "\x01\x05\x00\x00", 4);
return TDDL_SUCCESS;
case TDDL_CAP_VER_FW:
if (*puntCapBufLen < 4) return TDDL_E_INSUFFICIENT_BUFFER;
*puntCapBufLen = 4;
res = send_to_tpm((uint8_t*)"\x00\xc1\x00\x00\x00\x12\x00\x00\x00\x65"
"\x00\x00\x00\x06\x00\x00\x00\x00", 18);
if (res != TDDL_SUCCESS) return res;
res = receive_from_tpm(buf, &len);
if (res != TDDL_SUCCESS) return res;
if (len != 18 || (buf[6] | buf[7] | buf[8] | buf[9]) != 0) return TDDL_E_FAIL;
memcpy(pCapBuf, &buf[14], 4);
return TDDL_SUCCESS;
case TDDL_CAP_VER_FW_DATE:
/* this is not yet supported by the TPM emulator */
return TDDL_E_NOTIMPL;
default:
return TDDL_E_BAD_PARAMETER;
}
}
static TSS_RESULT cap_property(UINT32 SubCap, BYTE* pCapBuf,
UINT32* puntCapBufLen)
{
static const char *manufacturer = "Mario Strasser, ETH Zurich";
static const char *type = "Software-based TPM Emulator";
switch (SubCap) {
case TDDL_CAP_PROP_MANUFACTURER:
if (*puntCapBufLen < strlen(manufacturer))
return TDDL_E_INSUFFICIENT_BUFFER;
*puntCapBufLen = strlen(manufacturer);
memcpy(pCapBuf, manufacturer, *puntCapBufLen);
return TDDL_SUCCESS;
case TDDL_CAP_PROP_MODULE_TYPE:
if (*puntCapBufLen < strlen(type)) return TDDL_E_INSUFFICIENT_BUFFER;
*puntCapBufLen = strlen(type);
memcpy(pCapBuf, type, *puntCapBufLen);
return TDDL_SUCCESS;
default:
return TDDL_E_BAD_PARAMETER;
}
}
TSS_RESULT Tddli_GetCapability(UINT32 CapArea, UINT32 SubCap,
BYTE* pCapBuf, UINT32* puntCapBufLen)
{
TSS_RESULT res = TDDL_SUCCESS;
if (tddli_dh < 0) return TDDL_E_FAIL;
tddli_mutex_lock(&tddli_lock);
switch (CapArea) {
case TDDL_CAP_VERSION:
res = cap_version(SubCap, pCapBuf, puntCapBufLen);
break;
case TDDL_CAP_PROPERTY:
res = cap_property(SubCap, pCapBuf, puntCapBufLen);
break;
default:
res = TDDL_E_BAD_PARAMETER;
}
tddli_mutex_unlock(&tddli_lock);
return res;
}
TSS_RESULT Tddli_SetCapability(UINT32 CapArea, UINT32 SubCap,
BYTE* pCapBuf, UINT32* puntCapBufLen)
{
/* no vendor-specific capabilities available, yet */
return TDDL_E_BAD_PARAMETER;
}
TSS_RESULT Tddli_GetStatus(UINT32 ReqStatusType, UINT32* puntStatus)
{
TSS_RESULT res = TDDL_SUCCESS;
tddli_mutex_lock(&tddli_lock);
switch (ReqStatusType) {
case TDDL_DRIVER_STATUS:
*puntStatus = tddli_driver_status;
break;
case TDDL_DEVICE_STATUS:
*puntStatus = tddli_device_status;
break;
default:
res = TDDL_E_BAD_PARAMETER;
}
tddli_mutex_unlock(&tddli_lock);
return res;
}
TSS_RESULT Tddli_SetPowerManagement(TSS_BOOL SendSaveStateCommand,
UINT32 *QuerySetNewTPMPowerState)
{
return TDDL_E_NOTIMPL;
}
TSS_RESULT Tddli_PowerManagementControl(TSS_BOOL SendPowerManager,
UINT32 DriverManagesPowerStates)
{
return TDDL_E_NOTIMPL;
}
/*
* Export also TDDL_* function aliases as they are
* used by some non standard-conform applications.
*/
TSS_RESULT TDDL_Open()
{
return Tddli_Open();
}
TSS_RESULT TDDL_Close()
{
return Tddli_Close();
}
TSS_RESULT TDDL_Cancel()
{
return Tddli_Cancel();
}
TSS_RESULT TDDL_TransmitData(BYTE* pTransmitBuf, UINT32 TransmitBufLen,
BYTE* pReceiveBuf, UINT32* puntReceiveBufLen)
{
return Tddli_TransmitData(pTransmitBuf, TransmitBufLen,
pReceiveBuf, puntReceiveBufLen);
}
TSS_RESULT TDDL_GetCapability(UINT32 CapArea, UINT32 SubCap,
BYTE* pCapBuf, UINT32* puntCapBufLen)
{
return Tddli_GetCapability(CapArea, SubCap, pCapBuf, puntCapBufLen);
}
TSS_RESULT TDDL_SetCapability(UINT32 CapArea, UINT32 SubCap,
BYTE* pCapBuf, UINT32* puntCapBufLen)
{
return Tddli_SetCapability(CapArea, SubCap, pCapBuf, puntCapBufLen);
}
TSS_RESULT TDDL_GetStatus(UINT32 ReqStatusType, UINT32* puntStatus)
{
return Tddli_GetStatus(ReqStatusType, puntStatus);
}
TSS_RESULT TDDL_SetPowerManagement(TSS_BOOL SendSaveStateCommand,
UINT32 *QuerySetNewTPMPowerState)
{
return Tddli_SetPowerManagement(SendSaveStateCommand, QuerySetNewTPMPowerState);
}
TSS_RESULT TDDL_PowerManagementControl(TSS_BOOL SendPowerManager,
UINT32 DriverManagesPowerStates)
{
return Tddli_PowerManagementControl(SendPowerManager, DriverManagesPowerStates);
}