blob: 08a82101825f6d11eb5940308415396ba51e3101 [file] [log] [blame]
/**
* Copyright(c) 2011 Trusted Logic. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* * Neither the name Trusted Logic nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "tee_client_api.h"
#include "schannel6_protocol.h"
#include "s_error.h"
#include "s_version.h"
#include <stdlib.h>
#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include <stdarg.h>
#include <assert.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <unistd.h>
#include <linux/limits.h>
#include <time.h>
#include <sys/time.h>
/*
* SCX_VERSION_INFORMATION_BUFFER structure description
* Description of the sVersionBuffer handed over from user space to kernel space
* This field is filled after an IOCTL call and handed back to user space
*/
typedef struct
{
uint8_t sDriverDescription[65];
uint8_t sSecureWorldDescription[65];
} SCX_VERSION_INFORMATION_BUFFER;
/* The IOCTLs to the driver */
#define IOCTL_SCX_GET_VERSION \
_IO('z', 0)
#define IOCTL_SCX_EXCHANGE \
_IOWR('z', 1, SCHANNEL6_COMMAND)
#define IOCTL_SCX_GET_DESCRIPTION \
_IOR('z', 2, SCX_VERSION_INFORMATION_BUFFER)
/* Expected driver interface version. */
#define SM_DRIVER_VERSION 0x04000000
#define SCX_DEFAULT_DEVICE_NAME "tf_driver"
#define SCX_PARAM_TYPE_GET(nParamTypes, i) (((nParamTypes) >> (4*i)) & 0xF)
#define VAR_NOT_USED(variable) do{(void)(variable);}while(0);
#define SIZE_4KB 0x1000
#define SIZE_1MB 0x100000
/* ------------------------------------------------------------------------ */
/* UTILS */
/* ------------------------------------------------------------------------ */
#ifdef NDEBUG
/* Compile-out the traces */
#define TRACE_ERROR(...)
#define TRACE_WARNING(...)
#define TRACE_INFO(...)
#else
static void TRACE_ERROR(const char* format, ...)
{
va_list ap;
va_start(ap, format);
fprintf(stderr, "TRACE: ERROR: ");
vfprintf(stderr, format, ap);
fprintf(stderr, "\n");
va_end(ap);
}
static void TRACE_WARNING(const char* format, ...)
{
va_list ap;
va_start(ap, format);
fprintf(stderr, "TRACE: WARNING: ");
vfprintf(stderr, format, ap);
fprintf(stderr, "\n");
va_end(ap);
}
static void TRACE_INFO(const char* format, ...)
{
va_list ap;
va_start(ap, format);
fprintf(stderr, "TRACE: ");
vfprintf(stderr, format, ap);
fprintf(stderr, "\n");
va_end(ap);
}
#endif /* NDEBUG */
/*
* ====================================================
* Internal functions
* =====================================================
*/
static void scxYield(void)
{
sleep(0);
}
/* ------------------------------------------------------------------------ */
/*
* Exchange a message with the Secure World
* by calling the ioctl command of the linux driver
*
* @param pContext
* @param pCommand a SChannel command message that must have been filled except for the operation parameters
* @param pAnswer a placeholder for the SChannel answer
* @param pOperation a TEEC_Operation structure that contains the operation parameters (and types)
* and is updated with the SChannel answer data as appropriate. This parameter is
* used only for the open and invoke operations
*/
static TEEC_Result scxExchangeMessage(
IN TEEC_Context* pContext,
IN SCHANNEL6_COMMAND* pCommand,
OUT SCHANNEL6_ANSWER* pAnswer,
IN TEEC_Operation* pOperation)
{
TEEC_Result nResult = TEEC_SUCCESS;
TRACE_INFO("scxExchangeMessage[0x%X]\n",pContext);
if (pOperation != NULL)
{
/* Determine message parameters from operation parameters */
uint32_t i;
SCHANNEL6_COMMAND_PARAM* pSCXParams;
/* Note that nParamType is at the same position in an open and an invoke message */
pCommand->sHeader.nMessageInfo = pOperation->paramTypes;
if (pCommand->sHeader.nMessageType == SCX_OPEN_CLIENT_SESSION)
{
pSCXParams = pCommand->sOpenClientSession.sParams;
}
else
{
/* An invoke-command */
pSCXParams = pCommand->sInvokeClientCommand.sParams;
}
for (i = 0; i < 4; i++)
{
uint32_t nTEECParamType = SCX_PARAM_TYPE_GET(pOperation->paramTypes, i);
TEEC_Parameter* pTEECParam = &pOperation->params[i];
SCHANNEL6_COMMAND_PARAM* pSCXParam = &pSCXParams[i];
if (nTEECParamType & SCX_PARAM_TYPE_MEMREF_FLAG)
{
if (nTEECParamType & SCX_PARAM_TYPE_REGISTERED_MEMREF_FLAG)
{
/* A registered memref */
pSCXParam->sMemref.hBlock = pTEECParam->memref.parent->imp._hBlock;
if (nTEECParamType == TEEC_MEMREF_WHOLE)
{
/* A memref on the whole shared memory */
/* Set the direction from the shared memory flags */
pCommand->sInvokeClientCommand.nParamTypes |=
(pTEECParam->memref.parent->flags & (SCX_PARAM_TYPE_INPUT_FLAG | SCX_PARAM_TYPE_OUTPUT_FLAG))
<< (4*i);
pSCXParam->sMemref.nSize = pTEECParam->memref.parent->size;
pSCXParam->sMemref.nOffset = 0;
}
else
{
/* A partial memref */
pSCXParam->sMemref.nSize = pTEECParam->memref.size;
pSCXParam->sMemref.nOffset = pTEECParam->memref.offset;
}
}
else
{
/* A temporary memref */
/* Set nOffset to the address in the client. This allows the server
to allocate a block with the same alignment and also to
detect a NULL tmpref.
*/
pSCXParam->sTempMemref.nOffset = (uint32_t)pTEECParam->tmpref.buffer;
pSCXParam->sTempMemref.nDescriptor = (uint32_t)pTEECParam->tmpref.buffer;
pSCXParam->sTempMemref.nSize = pTEECParam->tmpref.size;
}
}
else if (nTEECParamType & SCX_PARAM_TYPE_INPUT_FLAG)
{
/* An input value */
pSCXParam->sValue.a = pTEECParam->value.a;
pSCXParam->sValue.b = pTEECParam->value.b;
}
}
}
pCommand->sHeader.nOperationID = (uint32_t)pAnswer;
nResult = ioctl((S_HANDLE)pContext->imp._hConnection, IOCTL_SCX_EXCHANGE, pCommand);
if (nResult != S_SUCCESS)
{
TRACE_INFO("scxExchangeMessage[0x%X]: Ioctl returned error: 0x%x (0x%x - %d)\n",pContext,nResult,errno,errno);
switch(errno)
{
case ENOMEM:
nResult=TEEC_ERROR_OUT_OF_MEMORY;
break;
case EACCES:
nResult=TEEC_ERROR_ACCESS_DENIED;
break;
default:
nResult=TEEC_ERROR_COMMUNICATION;
break;
}
}
if (pOperation != NULL)
{
/* Update the operation parameters from the answer message */
uint32_t i;
SCHANNEL6_ANSWER_PARAM * pSCXAnswers;
if (pAnswer->sHeader.nMessageType == SCX_OPEN_CLIENT_SESSION)
{
/* Open session */
pSCXAnswers = pAnswer->sOpenClientSession.sAnswers;
}
else
{
/* Invoke case */
pSCXAnswers = pAnswer->sInvokeClientCommand.sAnswers;
}
for (i = 0; i < 4; i++)
{
uint32_t nSCXParamType;
nSCXParamType = SCX_GET_PARAM_TYPE(pCommand->sHeader.nMessageInfo, i);
if (nSCXParamType & SCX_PARAM_TYPE_OUTPUT_FLAG)
{
if (nSCXParamType & SCX_PARAM_TYPE_MEMREF_FLAG)
{
/* Trick: the size field is at the same position in a memref or a tmpref */
pOperation->params[i].memref.size = pSCXAnswers[i].sSize.nSize;
}
else
{
/* An output value */
pOperation->params[i].value.a = pSCXAnswers[i].sValue.a;
pOperation->params[i].value.b = pSCXAnswers[i].sValue.b;
}
}
}
}
return nResult;
}
/* ------------------------------------------------------------------------ */
static void* scxAllocateSharedMemory(
IN uint32_t nLength)
{
if (nLength == 0)
{
/* This is valid, although we don't want to call mmap.
Just return a dummy non-NULL pointer */
return (void*)0x10;
}
else
{
return mmap(
0,nLength,
PROT_READ | PROT_WRITE,
MAP_SHARED | MAP_ANONYMOUS,
0,0);
}
}
/* ------------------------------------------------------------------------ */
static void scxReleaseSharedMemory(IN void* pBuffer,
IN uint32_t nLength)
{
if (nLength == 0)
{
return;
}
if (munmap(pBuffer, nLength)!= 0)
{
TRACE_WARNING("scxReleaseSharedMemory returned 0x%x \n",errno);
}
}
/* ------------------------------------------------------------------------ */
uint64_t scxGetCurrentTime(void)
{
uint64_t currentTime = 0;
struct timeval now;
gettimeofday(&now,NULL);
currentTime = now.tv_sec;
currentTime = (currentTime * 1000) + (now.tv_usec / 1000);
return currentTime;
}
/* ------------------------------------------------------------------------ */
/*
* ====================================================
* TEE Client API
* =====================================================
*/
/**
* Get a time-limit equal to now + relative timeout expressed in milliseconds.
**/
void TEEC_GetTimeLimit(
TEEC_Context* sContext,
uint32_t nTimeout,
TEEC_TimeLimit* sTimeLimit)
{
uint64_t nTimeLimit = 0;
VAR_NOT_USED(sContext);
TRACE_INFO("TEEC_GetTimeLimit(0x%X, %u ms)", sContext, nTimeout);
if (nTimeout == 0xFFFFFFFF )
{
/* Infinite timeout */
nTimeLimit = SCTIME_INFINITE;
}
else
{
nTimeLimit = scxGetCurrentTime() + nTimeout;
}
TRACE_INFO("GetTimeLimit %ld\n",nTimeLimit);
memcpy(sTimeLimit, &nTimeLimit, sizeof(TEEC_TimeLimit));
}
//-----------------------------------------------------------------------------------------------------
TEEC_Result TEEC_InitializeContext(
const char* pDeviceName,
TEEC_Context* pContext)
{
TEEC_Result nError = TEEC_SUCCESS;
S_HANDLE hDriver = S_HANDLE_NULL;
char sFullDeviceName[PATH_MAX];
uint32_t nVersion;
if(pDeviceName == NULL)
{
pDeviceName = SCX_DEFAULT_DEVICE_NAME;
}
strcpy(sFullDeviceName, "/dev/");
strcat(sFullDeviceName, pDeviceName);
hDriver = open(sFullDeviceName, O_RDWR, 0);
if (hDriver == (uint32_t)-1)
{
TRACE_ERROR("scxOpen: open() failed 0x%x\n", errno);
switch(errno)
{
case ENOMEM:
nError = TEEC_ERROR_OUT_OF_MEMORY;
goto error;
case EINTR:
break;
default:
nError = TEEC_ERROR_COMMUNICATION;
goto error;
}
}
fcntl(hDriver, F_SETFD, FD_CLOEXEC);
nVersion = ioctl(hDriver, IOCTL_SCX_GET_VERSION);
if (nVersion != SM_DRIVER_VERSION)
{
TRACE_ERROR("scxOpen: Not expected driver version: 0x%x instead of 0x%x\n", nVersion,SM_DRIVER_VERSION);
switch(errno)
{
case ENOMEM:
nError=TEEC_ERROR_OUT_OF_MEMORY;
break;
default:
nError=TEEC_ERROR_COMMUNICATION;
break;
}
close(hDriver);
}
error:
if(nError == TEEC_SUCCESS)
{
pContext->imp._hConnection = hDriver;
}
else
{
TRACE_ERROR("scxOpen failed 0x%x\n", nError);
pContext->imp._hConnection = 0;
}
return nError;
}
//-----------------------------------------------------------------------------------------------------
void TEEC_FinalizeContext(TEEC_Context* pContext)
{
TRACE_INFO("TEEC_FinalizeContext[0x%X]", pContext);
if (pContext == NULL) return;
close(pContext->imp._hConnection);
pContext->imp._hConnection = 0;
}
//-----------------------------------------------------------------------------------------------------
TEEC_Result TEEC_OpenSession (
TEEC_Context* context,
TEEC_Session* session, /* OUT */
const TEEC_UUID* destination, /* The trusted application UUID we want to open the session with */
uint32_t connectionMethod, /* LoginType*/
void* connectionData, /* LoginData */
TEEC_Operation* operation, /* payload. If operation is NULL then no data buffers are exchanged with the Trusted Application, and the operation cannot be cancelled by the Client Application */
uint32_t* errorOrigin)
{
return TEEC_OpenSessionEx(context,
session,
NULL,
destination,
connectionMethod,
connectionData,
operation,
errorOrigin);
}
//-----------------------------------------------------------------------------------------------------
void TEEC_CloseSession (TEEC_Session* session)
{
TEEC_Context* context;
SCHANNEL6_ANSWER sAnswer;
SCHANNEL6_COMMAND sCommand;
if (session == NULL) return;
context = session->imp._pContext;
memset(&sCommand,0,sizeof(sCommand));
sCommand.sHeader.nMessageType = SCX_CLOSE_CLIENT_SESSION;
sCommand.sHeader.nMessageSize = (sizeof(SCHANNEL6_CLOSE_CLIENT_SESSION_COMMAND)
- sizeof(SCHANNEL6_COMMAND_HEADER))/sizeof(uint32_t);
sCommand.sCloseClientSession.hClientSession = session->imp._hClientSession;
scxExchangeMessage(context, &sCommand, &sAnswer, NULL);
/* we ignore the error code of scxExchangeMessage */
session->imp._hClientSession = S_HANDLE_NULL;
session->imp._pContext = NULL;
}
//-----------------------------------------------------------------------------------------------------
TEEC_Result TEEC_InvokeCommand(
TEEC_Session* session,
uint32_t commandID,
TEEC_Operation* operation,
uint32_t* errorOrigin)
{
return TEEC_InvokeCommandEx(session,
NULL,
commandID,
operation,
errorOrigin);
}
//-----------------------------------------------------------------------------------------------------
/* Used to implement both register and allocate */
static TEEC_Result TEEC_RegisterSharedMemory0(
TEEC_Context* context,
TEEC_SharedMemory* sharedMem)
{
TEEC_Result nResult;
SCHANNEL6_COMMAND sCommand;
SCHANNEL6_ANSWER sAnswer;
TRACE_INFO("TEEC_RegisterSharedMemory0 (%p, %p)",context, sharedMem);
memset(&sCommand, 0, sizeof(sCommand));
sCommand.sRegisterSharedMemory.nMessageSize = (sizeof(SCHANNEL6_REGISTER_SHARED_MEMORY_COMMAND) - sizeof(SCHANNEL6_COMMAND_HEADER))/4;
sCommand.sRegisterSharedMemory.nMessageType = SCX_REGISTER_SHARED_MEMORY;
sCommand.sRegisterSharedMemory.nMemoryFlags = sharedMem->flags;
sCommand.sRegisterSharedMemory.nSharedMemSize = sharedMem->size;
sCommand.sRegisterSharedMemory.nSharedMemStartOffset = 0;
sCommand.sRegisterSharedMemory.nSharedMemDescriptors[0] = (uint32_t)sharedMem->buffer;
nResult = scxExchangeMessage(context,
&sCommand,
&sAnswer,
NULL);
if (nResult == TEEC_SUCCESS)
{
nResult = sAnswer.sRegisterSharedMemory.nErrorCode;
}
if (nResult == TEEC_SUCCESS)
{
sharedMem->imp._pContext = context;
sharedMem->imp._hBlock = sAnswer.sRegisterSharedMemory.hBlock;
}
return nResult;
}
//-----------------------------------------------------------------------------------------------------
TEEC_Result TEEC_RegisterSharedMemory(
TEEC_Context* context,
TEEC_SharedMemory* sharedMem)
{
TRACE_INFO("TEEC_RegisterSharedMemory (%p)",context);
sharedMem->imp._pContext = NULL;
sharedMem->imp._hBlock = S_HANDLE_NULL;
sharedMem->imp._bAllocated = false;
return TEEC_RegisterSharedMemory0(context, sharedMem);
}
//-----------------------------------------------------------------------------------------------------
TEEC_Result TEEC_AllocateSharedMemory(
TEEC_Context* context,
TEEC_SharedMemory* sharedMem)
{
TEEC_Result nResult;
TRACE_INFO("TEEC_AllocateSharedMemory (%p)",context);
sharedMem->imp._pContext = NULL;
sharedMem->imp._hBlock = S_HANDLE_NULL;
sharedMem->buffer = scxAllocateSharedMemory(sharedMem->size);
if (sharedMem->buffer == NULL)
{
return TEEC_ERROR_OUT_OF_MEMORY;
}
sharedMem->imp._bAllocated = true;
nResult = TEEC_RegisterSharedMemory0(context, sharedMem);
if (nResult != TEEC_SUCCESS)
{
scxReleaseSharedMemory(sharedMem->buffer,sharedMem->size);
sharedMem->buffer = NULL;
}
return nResult;
}
//-----------------------------------------------------------------------------------------------------
void TEEC_ReleaseSharedMemory (
TEEC_SharedMemory* sharedMem)
{
SCHANNEL6_ANSWER sAnswer;
SCHANNEL6_COMMAND sMessage;
TEEC_Context* context;
context = (TEEC_Context *)sharedMem->imp._pContext;
memset(&sMessage, 0, sizeof(SCHANNEL6_COMMAND));
sMessage.sReleaseSharedMemory.nMessageType = SCX_RELEASE_SHARED_MEMORY;
sMessage.sReleaseSharedMemory.nMessageSize = (sizeof(SCHANNEL6_RELEASE_SHARED_MEMORY_COMMAND)
- sizeof(SCHANNEL6_COMMAND_HEADER))/sizeof(uint32_t);
sMessage.sReleaseSharedMemory.hBlock = sharedMem->imp._hBlock;
scxExchangeMessage(context,&sMessage, &sAnswer, NULL);
if (sharedMem->imp._bAllocated)
{
scxReleaseSharedMemory(sharedMem->buffer,sharedMem->size);
/* Update parameters:
* In this case the Implementation MUST set the buffer and size fields of the sharedMem structure
* to NULL and 0 respectively before returning.
*/
sharedMem->buffer = NULL;
sharedMem->size = 0;
}
sharedMem->imp._pContext = NULL;
sharedMem->imp._hBlock = S_HANDLE_NULL;
}
//-----------------------------------------------------------------------------------------------------
void TEEC_RequestCancellation(TEEC_Operation* operation)
{
uint32_t nOperationState;
TEEC_Result nResult;
if (operation == NULL) return;
retry:
nOperationState = operation->started;
if (nOperationState == 2)
{
/* Operation already finished. Return immediately */
return;
}
else if (nOperationState == 1)
{
/* Operation is in progress */
TEEC_Context* context;
SCHANNEL6_ANSWER sAnswer;
SCHANNEL6_COMMAND sMessage;
context = operation->imp._pContext;
memset(&sMessage,0,sizeof(sMessage));
sMessage.sHeader.nMessageType = SCX_CANCEL_CLIENT_OPERATION;
sMessage.sHeader.nMessageSize = (sizeof(SCHANNEL6_CANCEL_CLIENT_OPERATION_COMMAND) - sizeof(SCHANNEL6_COMMAND_HEADER))/4;
sMessage.sCancelClientOperation.hClientSession = operation->imp._hSession;
sMessage.sCancelClientOperation.nCancellationID = (uint32_t)operation;
nResult = scxExchangeMessage(context,&sMessage, &sAnswer, NULL);
if (nResult != TEEC_SUCCESS)
{
/* Communication failure. Ignore the error: the operation is already cancelled anyway */
return;
}
if (sAnswer.sCancelClientOperation.nErrorCode == S_SUCCESS)
{
/* Command was successfully cancelled */
return;
}
/* Otherwise, the command has not yet reached the secure world or has already finished and we must retry */
}
/* This applies as well when nOperationState == 0. In this case, the operation has not yet
started yet and we don't even have a pointer to the context */
scxYield();
goto retry;
}
//-----------------------------------------------------------------------------------------------------
TEEC_Result TEEC_ReadSignatureFile(
void** ppSignatureFile,
uint32_t* pnSignatureFileLength)
{
TEEC_Result nErrorCode = TEEC_SUCCESS;
uint32_t nBytesRead;
uint32_t nSignatureSize = 0;
uint8_t* pSignature = NULL;
FILE* pSignatureFile = NULL;
char sFileName[PATH_MAX + 1 + 5]; /* Allocate room for the signature extension */
long nFileSize;
*pnSignatureFileLength = 0;
*ppSignatureFile = NULL;
if (realpath("/proc/self/exe", sFileName) == NULL)
{
TRACE_ERROR("TEEC_ReadSignatureFile: realpath failed [%d]", errno);
return TEEC_ERROR_OS;
}
/* Work out the signature file name */
strcat(sFileName, ".ssig");
pSignatureFile = fopen(sFileName, "rb");
if (pSignatureFile == NULL)
{
/* Signature doesn't exist */
return TEEC_ERROR_ITEM_NOT_FOUND;
}
if (fseek(pSignatureFile, 0, SEEK_END) != 0)
{
TRACE_ERROR("TEEC_ReadSignatureFile: fseek(%s) failed [%d]",
sFileName, errno);
nErrorCode = TEEC_ERROR_OS;
goto error;
}
nFileSize = ftell(pSignatureFile);
if (nFileSize < 0)
{
TRACE_ERROR("TEEC_ReadSignatureFile: ftell(%s) failed [%d]",
sFileName, errno);
nErrorCode = TEEC_ERROR_OS;
goto error;
}
nSignatureSize = (uint32_t)nFileSize;
if (nSignatureSize != 0)
{
pSignature = malloc(nSignatureSize);
if (pSignature == NULL)
{
TRACE_ERROR("TEEC_ReadSignatureFile: Heap - Out of memory for %u bytes",
nSignatureSize);
nErrorCode = TEEC_ERROR_OUT_OF_MEMORY;
goto error;
}
rewind(pSignatureFile);
nBytesRead = fread(pSignature, 1, nSignatureSize, pSignatureFile);
if (nBytesRead < nSignatureSize)
{
TRACE_ERROR("TEEC_ReadSignatureFile: fread failed [%d]", errno);
nErrorCode = TEEC_ERROR_OS;
goto error;
}
}
fclose(pSignatureFile);
*pnSignatureFileLength = nSignatureSize;
*ppSignatureFile = pSignature;
return S_SUCCESS;
error:
fclose(pSignatureFile);
free(pSignature);
return nErrorCode;
}
//-----------------------------------------------------------------------------------------------------
TEEC_Result TEEC_OpenSessionEx (
TEEC_Context* context,
TEEC_Session* session, /* OUT */
const TEEC_TimeLimit* timeLimit,
const TEEC_UUID* destination, /* The trusted application UUID we want to open the session with */
uint32_t connectionMethod, /* LoginType*/
void* connectionData, /* LoginData */
TEEC_Operation* operation, /* payload. If operation is NULL then no data buffers are exchanged with the Trusted Application, and the operation cannot be cancelled by the Client Application */
uint32_t* returnOrigin)
{
TEEC_Result nError;
uint32_t nReturnOrigin;
SCHANNEL6_ANSWER sAnswer;
SCHANNEL6_COMMAND sCommand;
memset(&sCommand, 0, sizeof(SCHANNEL6_COMMAND));
sCommand.sHeader.nMessageType = SCX_OPEN_CLIENT_SESSION;
sCommand.sHeader.nMessageSize = (sizeof(SCHANNEL6_OPEN_CLIENT_SESSION_COMMAND) - 20 -sizeof(SCHANNEL6_COMMAND_HEADER))/sizeof(uint32_t);
if (timeLimit == NULL)
{
sCommand.sOpenClientSession.sTimeout = SCTIME_INFINITE;
}
else
{
sCommand.sOpenClientSession.sTimeout = *(uint64_t*)timeLimit;
}
sCommand.sOpenClientSession.sDestinationUUID = *((S_UUID*)destination);
sCommand.sOpenClientSession.nLoginType = connectionMethod;
if ((connectionMethod == TEEC_LOGIN_GROUP)||(connectionMethod == TEEC_LOGIN_GROUP_APPLICATION))
{
/* connectionData MUST point to a uint32_t which contains the group
* which this Client Application wants to connect as. The Linux Driver
* is responsible for securely ensuring that the Client Application
* instance is actually a member of this group.
*/
if (connectionData != NULL)
{
*(uint32_t*)sCommand.sOpenClientSession.sLoginData = *(uint32_t*)connectionData;
sCommand.sHeader.nMessageSize += sizeof(uint32_t);
}
}
sCommand.sOpenClientSession.nCancellationID = (uint32_t)operation; // used for TEEC_RequestCancellation
if (operation != NULL)
{
operation->imp._pContext = context;
operation->imp._hSession = S_HANDLE_NULL;
operation->started = 1;
}
nError = scxExchangeMessage(context, &sCommand, &sAnswer, operation);
if (operation != NULL) operation->started = 2;
if (nError != TEEC_SUCCESS)
{
nReturnOrigin = TEEC_ORIGIN_COMMS;
}
else
{
nError = sAnswer.sOpenClientSession.nErrorCode;
nReturnOrigin = sAnswer.sOpenClientSession.nReturnOrigin;
}
if (returnOrigin != NULL) *returnOrigin = nReturnOrigin;
if (nError == S_SUCCESS)
{
session->imp._hClientSession = sAnswer.sOpenClientSession.hClientSession;
session->imp._pContext = context;
}
return nError;
}
//-----------------------------------------------------------------------------------------------------
TEEC_Result TEEC_InvokeCommandEx(
TEEC_Session* session,
const TEEC_TimeLimit* timeLimit,
uint32_t commandID,
TEEC_Operation* operation,
uint32_t* returnOrigin)
{
TEEC_Result nError;
SCHANNEL6_ANSWER sAnswer;
SCHANNEL6_COMMAND sCommand;
uint32_t nReturnOrigin;
TEEC_Context * context;
context = (TEEC_Context *)session->imp._pContext;
memset(&sCommand, 0, sizeof(SCHANNEL6_COMMAND));
sCommand.sHeader.nMessageType = SCX_INVOKE_CLIENT_COMMAND;
sCommand.sHeader.nMessageSize = (sizeof(SCHANNEL6_INVOKE_CLIENT_COMMAND_COMMAND) - sizeof(SCHANNEL6_COMMAND_HEADER))/sizeof(uint32_t);
sCommand.sInvokeClientCommand.nClientCommandIdentifier = commandID;
if (timeLimit == NULL)
{
sCommand.sInvokeClientCommand.sTimeout = SCTIME_INFINITE;
}
else
{
sCommand.sInvokeClientCommand.sTimeout = *(uint64_t*)timeLimit;
}
sCommand.sInvokeClientCommand.hClientSession = session->imp._hClientSession;
sCommand.sInvokeClientCommand.nCancellationID = (uint32_t)operation; // used for TEEC_RequestCancellation
if (operation != NULL)
{
operation->imp._pContext = session->imp._pContext;
operation->imp._hSession = session->imp._hClientSession;
operation->started = 1;
}
nError = scxExchangeMessage(context, &sCommand, &sAnswer, operation);
if (operation != NULL)
{
operation->started = 2;
operation->imp._hSession = S_HANDLE_NULL;
operation->imp._pContext = NULL;
}
if (nError != TEEC_SUCCESS)
{
nReturnOrigin = TEEC_ORIGIN_COMMS;
}
else
{
nError = sAnswer.sInvokeClientCommand.nErrorCode;
nReturnOrigin = sAnswer.sInvokeClientCommand.nReturnOrigin;
}
if (returnOrigin != NULL) *returnOrigin = nReturnOrigin;
return nError;
}
//-----------------------------------------------------------------------------------------------------
/*
* Retrieves information about the implementation
*/
void TEEC_GetImplementationInfo(
TEEC_Context* context,
TEEC_ImplementationInfo* description)
{
TRACE_INFO("TEEC_GetImplementationInfo");
memset(description, 0, sizeof(TEEC_ImplementationInfo));
strcpy(description->apiDescription, S_VERSION_STRING);
if (context != NULL)
{
SCX_VERSION_INFORMATION_BUFFER sInfoBuffer;
uint32_t nResult;
nResult = ioctl((S_HANDLE)context->imp._hConnection, IOCTL_SCX_GET_DESCRIPTION, &sInfoBuffer);
if (nResult != S_SUCCESS)
{
TRACE_ERROR("TEEC_GetImplementationInfo[0x%X]: ioctl returned error: 0x%x ( %d)\n",context, nResult, errno);
return;
}
memcpy(description->commsDescription, sInfoBuffer.sDriverDescription, 64);
description->commsDescription[64] = 0;
memcpy(description->TEEDescription, sInfoBuffer.sSecureWorldDescription, 64);
description->TEEDescription[64] = 0;
}
}
void TEEC_GetImplementationLimits(
TEEC_ImplementationLimits* limits)
{
memset(limits, 0, sizeof(TEEC_ImplementationLimits));
/* A temp mem ref can not be mapped on more than 1Mb */
limits->pageSize = SIZE_4KB;
limits->tmprefMaxSize = SIZE_1MB;
limits->sharedMemMaxSize = SIZE_1MB * 8;
}