summaryrefslogtreecommitdiffstats
path: root/security/tf_daemon/delegation_client.c
diff options
context:
space:
mode:
Diffstat (limited to 'security/tf_daemon/delegation_client.c')
-rw-r--r--security/tf_daemon/delegation_client.c1401
1 files changed, 1401 insertions, 0 deletions
diff --git a/security/tf_daemon/delegation_client.c b/security/tf_daemon/delegation_client.c
new file mode 100644
index 0000000..54ee112
--- /dev/null
+++ b/security/tf_daemon/delegation_client.c
@@ -0,0 +1,1401 @@
+/**
+ * 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.
+ */
+
+#if defined(__ANDROID32__)
+#include <stddef.h>
+#endif
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+/*
+ * When porting to a new OS, insert here the appropriate include files
+ */
+#include <sys/stat.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <fcntl.h>
+
+#if defined(LINUX) || defined(__ANDROID32__)
+#include <unistd.h>
+#include <sys/resource.h>
+
+
+#if defined(__ANDROID32__)
+/* fdatasync does not exist on Android */
+#define fdatasync fsync
+#else
+/*
+ * http://linux.die.net/man/2/fsync
+ * The function fdatasync seems to be absent of the header file
+ * in some distributions
+ */
+int fdatasync(int fd);
+#endif /* __ANDROID32__ */
+#include <syslog.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <pthread.h>
+#include <semaphore.h>
+#define PATH_SEPARATOR '/'
+#endif /* LINUX || __ANDROID32__ */
+
+#ifdef WIN32
+#include <windows.h>
+#include <io.h>
+#define PATH_SEPARATOR '\\'
+#endif
+
+#ifdef __SYMBIAN32__
+#include <unistd.h>
+#include "os_symbian.h"
+#define PATH_SEPARATOR '\\'
+#endif
+
+#include <stdarg.h>
+#include <assert.h>
+
+#include "service_delegation_protocol.h"
+
+#include "s_version.h"
+#include "s_error.h"
+#include "tee_client_api.h"
+
+/* You can define the preprocessor constant SUPPORT_DELEGATION_EXTENSION
+ if you want to pass extended options in a configuration file (option '-c').
+ It is up to you to define the format of this configuration file and the
+ extended option in the source file delegation_client_extension.c. You can
+ use extended options, e.g., to control the name of each partition file. */
+#ifdef SUPPORT_DELEGATION_EXTENSION
+#include "delegation_client_extension.h"
+#endif
+
+/*----------------------------------------------------------------------------
+ * Design notes
+ * ============
+ *
+ * This implementation of the delegation daemon supports the protocol
+ * specified in the Product Reference Manual ("Built-in Services Protocols Specification")
+ *
+ *----------------------------------------------------------------------------*/
+
+/*----------------------------------------------------------------------------
+ * Defines and structures
+ *----------------------------------------------------------------------------*/
+#define ECHANGE_BUFFER_INSTRUCTIONS_NB 100
+
+#define DEFAULT_WORKSPACE_SIZE (128*1024)
+
+/* A single shared memory block is used to contain the administrative data, the
+ instruction buffer and the workspace. The size of the instruction buffer is
+ fixed, but the size of workspace can be configured using the "-workspaceSize"
+ command-line option. */
+typedef struct
+{
+ DELEGATION_ADMINISTRATIVE_DATA sAdministrativeData;
+ uint32_t sInstructions[ECHANGE_BUFFER_INSTRUCTIONS_NB];
+ uint8_t sWorkspace[1/*g_nWorkspaceSize*/];
+} DELEGATION_EXCHANGE_BUFFER;
+
+#define MD_VAR_NOT_USED(variable) do{(void)(variable);}while(0);
+
+#define MD_INLINE __inline
+
+/* ----------------------------------------------
+ Traces and logs
+
+ On Linux, traces and logs go either to the console (stderr) or to the syslog.
+ When the daemon is started, the logs go to the console. Once and if the daemon
+ is detached, the logs go to syslog.
+
+ On other systems, traces and logs go systematically to stderr
+
+ The difference between traces and logs is that traces are compiled out
+ in release builds whereas logs are visible to the customer.
+
+ -----------------------------------------------*/
+#if defined(LINUX) || (defined __ANDROID32__)
+
+static bool bDetached = false;
+
+static MD_INLINE void LogError(const char* format, ...)
+{
+ va_list ap;
+ va_start(ap, format);
+ if (bDetached)
+ {
+ vsyslog(LOG_ERR, format, ap);
+ }
+ else
+ {
+ fprintf(stderr, "ERROR: ");
+ vfprintf(stderr, format, ap);
+ fprintf(stderr, "\n");
+ }
+ va_end(ap);
+}
+
+static MD_INLINE void LogWarning(const char* format, ...)
+{
+ va_list ap;
+ va_start(ap, format);
+ if (bDetached)
+ {
+ vsyslog(LOG_WARNING, format, ap);
+ }
+ else
+ {
+ fprintf(stderr, "WARNING: ");
+ vfprintf(stderr, format, ap);
+ fprintf(stderr, "\n");
+ }
+ va_end(ap);
+}
+static MD_INLINE void LogInfo(const char* format, ...)
+{
+ va_list ap;
+ va_start(ap, format);
+ if (bDetached)
+ {
+ vsyslog(LOG_INFO, format, ap);
+ }
+ else
+ {
+ vfprintf(stderr, format, ap);
+ fprintf(stderr, "\n");
+ }
+ va_end(ap);
+}
+
+static MD_INLINE void TRACE_ERROR(const char* format, ...)
+{
+#ifndef NDEBUG
+ va_list ap;
+ va_start(ap, format);
+ if (bDetached)
+ {
+ vsyslog(LOG_ERR, format, ap);
+ }
+ else
+ {
+ fprintf(stderr, "TRACE: ERROR: ");
+ vfprintf(stderr, format, ap);
+ fprintf(stderr, "\n");
+ }
+ va_end(ap);
+#else
+ MD_VAR_NOT_USED(format);
+#endif /* NDEBUG */
+}
+
+static MD_INLINE void TRACE_WARNING(const char* format, ...)
+{
+#ifndef NDEBUG
+ va_list ap;
+ va_start(ap, format);
+ if (bDetached)
+ {
+ vsyslog(LOG_WARNING, format, ap);
+ }
+ else
+ {
+ fprintf(stderr, "TRACE: WARNING: ");
+ vfprintf(stderr, format, ap);
+ fprintf(stderr, "\n");
+ }
+ va_end(ap);
+#else
+ MD_VAR_NOT_USED(format);
+#endif /* NDEBUG */
+}
+
+static MD_INLINE void TRACE_INFO(const char* format, ...)
+{
+#ifndef NDEBUG
+ va_list ap;
+ va_start(ap, format);
+ if (bDetached)
+ {
+ vsyslog(LOG_DEBUG, format, ap);
+ }
+ else
+ {
+ fprintf(stderr, "TRACE: ");
+ vfprintf(stderr, format, ap);
+ fprintf(stderr, "\n");
+ }
+ va_end(ap);
+#else
+ MD_VAR_NOT_USED(format);
+#endif /* NDEBUG */
+}
+#elif defined __SYMBIAN32__
+/* defined in os_symbian.h */
+
+#elif defined NO_LOG_NO_TRACE
+static MD_INLINE void LogError(const char* format, ...)
+{
+ MD_VAR_NOT_USED(format);
+}
+static MD_INLINE void LogWarning(const char* format, ...)
+{
+ MD_VAR_NOT_USED(format);
+}
+static MD_INLINE void LogInfo(const char* format, ...)
+{
+ MD_VAR_NOT_USED(format);
+}
+
+static MD_INLINE void TRACE_ERROR(const char* format, ...)
+{
+ MD_VAR_NOT_USED(format);
+}
+
+static MD_INLINE void TRACE_WARNING(const char* format, ...)
+{
+ MD_VAR_NOT_USED(format);
+}
+
+static MD_INLINE void TRACE_INFO(const char* format, ...)
+{
+ MD_VAR_NOT_USED(format);
+}
+
+#else
+/* !defined(LINUX) || !defined(__ANDROID32__) */
+
+static MD_INLINE void LogError(const char* format, ...)
+{
+ va_list ap;
+ va_start(ap, format);
+ fprintf(stderr, "ERROR: ");
+ vfprintf(stderr, format, ap);
+ fprintf(stderr, "\n");
+ va_end(ap);
+}
+static MD_INLINE void LogWarning(const char* format, ...)
+{
+ va_list ap;
+ va_start(ap, format);
+ fprintf(stderr, "WARNING: ");
+ vfprintf(stderr, format, ap);
+ fprintf(stderr, "\n");
+ va_end(ap);
+}
+static MD_INLINE void LogInfo(const char* format, ...)
+{
+ va_list ap;
+ va_start(ap, format);
+ vfprintf(stderr, format, ap);
+ fprintf(stderr, "\n");
+ va_end(ap);
+}
+
+static MD_INLINE void TRACE_ERROR(const char* format, ...)
+{
+#ifndef NDEBUG
+ va_list ap;
+ va_start(ap, format);
+ fprintf(stderr, "TRACE: ERROR: ");
+ vfprintf(stderr, format, ap);
+ fprintf(stderr, "\n");
+ va_end(ap);
+#else
+ MD_VAR_NOT_USED(format);
+#endif /* NDEBUG */
+}
+
+static MD_INLINE void TRACE_WARNING(const char* format, ...)
+{
+#ifndef NDEBUG
+ va_list ap;
+ va_start(ap, format);
+ fprintf(stderr, "TRACE: WARNING: ");
+ vfprintf(stderr, format, ap);
+ fprintf(stderr, "\n");
+ va_end(ap);
+#else
+ MD_VAR_NOT_USED(format);
+#endif /* NDEBUG */
+}
+
+static MD_INLINE void TRACE_INFO(const char* format, ...)
+{
+#ifndef NDEBUG
+ va_list ap;
+ va_start(ap, format);
+ fprintf(stderr, "TRACE: ");
+ vfprintf(stderr, format, ap);
+ fprintf(stderr, "\n");
+ va_end(ap);
+#else
+ MD_VAR_NOT_USED(format);
+#endif /* NDEBUG */
+}
+#endif /* defined(LINUX) || defined(__ANDROID32__) */
+
+/*----------------------------------------------------------------------------
+ * Globals
+ *----------------------------------------------------------------------------*/
+/* The sector size */
+static uint32_t g_nSectorSize;
+
+/* The workspace size */
+static uint32_t g_nWorkspaceSize = DEFAULT_WORKSPACE_SIZE;
+
+/* UUID of the delegation service */
+static const TEEC_UUID g_sServiceId = SERVICE_DELEGATION_UUID;
+
+/* pWorkspaceBuffer points to the workspace buffer shared with the secure
+ world to transfer the sectors in the READ and WRITE instructions */
+static uint8_t* g_pWorkspaceBuffer;
+static DELEGATION_EXCHANGE_BUFFER * g_pExchangeBuffer;
+TEEC_SharedMemory sExchangeSharedMem;
+/*
+ The absolute path name for each of the 16 possible partitions.
+ */
+static char* g_pPartitionNames[16];
+
+/* The file context for each of the 16 possible partitions. An entry
+ in this array is NULL if the corresponding partition is currently not opened
+ */
+static FILE* g_pPartitionFiles[16];
+
+/*----------------------------------------------------------------------------
+ * Utilities functions
+ *----------------------------------------------------------------------------*/
+static void printUsage(void)
+{
+ LogInfo("usage : tf_daemon [options]");
+ LogInfo("where [options] are:");
+ LogInfo("-h --help Display help.");
+#ifdef SUPPORT_DELEGATION_EXTENSION
+ LogInfo("-c <conf> Configuration file path.");
+#else
+ /* If the compilation parameter SUPPORT_DELEGATION_EXTENSION is not set, each
+ partition is stored as a file within the base dir */
+ LogInfo("-storageDir <baseDir> Set the directory where the data will be stored; this directory");
+ LogInfo(" must be writable and executable (this parameter is mandatory)");
+#endif
+ LogInfo("-d Turns on debug mode. If not specified, the daemon will fork itself");
+ LogInfo(" and get detached from the console.");
+#ifndef SUPPORT_DELEGATION_EXTENSION
+ LogInfo("-workspaceSize <integer> Set the size in bytes of the workspace. Must be greater or equal to 8 sectors.");
+ LogInfo(" (default is 128KB)");
+#endif
+}
+
+static TEEC_Result errno2serror(void)
+{
+ switch (errno)
+ {
+ case EINVAL:
+ return S_ERROR_BAD_PARAMETERS;
+ case EMFILE:
+ return S_ERROR_NO_MORE_HANDLES;
+ case ENOENT:
+ return S_ERROR_ITEM_NOT_FOUND;
+ case EEXIST:
+ return S_ERROR_ITEM_EXISTS;
+ case ENOSPC:
+ return S_ERROR_STORAGE_NO_SPACE;
+ case ENOMEM:
+ return S_ERROR_OUT_OF_MEMORY;
+ case EBADF:
+ case EACCES:
+ default:
+ return S_ERROR_STORAGE_UNREACHABLE;
+ }
+}
+
+/*
+ * Check if the directory in parameter exists with Read/Write access
+ * Return 0 in case of success and 1 otherwise.
+ */
+int static_checkStorageDirAndAccessRights(char * directoryName)
+{
+#ifdef __SYMBIAN32__
+ /* it looks like stat is not working properly on Symbian
+ Create and remove dummy file to check access rights */
+ FILE *stream;
+ char *checkAccess = NULL;
+
+ if (directoryName == NULL)
+ {
+ LogError("Directory Name is NULL");
+ return 1;
+ }
+
+ checkAccess = malloc(strlen(directoryName)+1/* \ */ +1 /* a */ + 1 /* 0 */);
+ if (!checkAccess)
+ {
+ LogError("storageDir '%s' allocation error", directoryName);
+ return 1;
+ }
+ sprintf(checkAccess,"%s\\a",directoryName);
+ stream = fopen(checkAccess, "w+b");
+ if (!stream)
+ {
+ LogError("storageDir '%s' is incorrect or cannot be reached", directoryName);
+ return 1;
+ }
+ fclose(stream);
+ unlink(checkAccess);
+#else
+ /* Non-Symbian OS: use stat */
+ struct stat buf;
+ int result = 0;
+
+ if (directoryName == NULL)
+ {
+ LogError("Directory Name is NULL");
+ return 1;
+ }
+
+ result = stat(directoryName, &buf);
+ if (result == 0)
+ {
+ /* Storage dir exists. Check access rights */
+#if defined(LINUX) || (defined __ANDROID32__)
+ if ((buf.st_mode & (S_IXUSR | S_IWUSR)) != (S_IXUSR | S_IWUSR))
+ {
+ LogError("storageDir '%s' does not have read-write access", directoryName);
+ return 1;
+ }
+#endif
+ }
+ else if (errno == ENOENT)
+ {
+ LogError("storageDir '%s' does not exist", directoryName);
+ return 1;
+ }
+ else
+ {
+ /* Another error */
+ LogError("storageDir '%s' is incorrect or cannot be reached", directoryName);
+ return 1;
+ }
+#endif
+ return 0;
+}
+
+
+
+/*----------------------------------------------------------------------------
+ * Instructions
+ *----------------------------------------------------------------------------*/
+
+/**
+ * This function executes the DESTROY_PARTITION instruction
+ *
+ * @param nPartitionID: the partition identifier
+ **/
+static TEEC_Result partitionDestroy(uint32_t nPartitionID)
+{
+ TEEC_Result nError = S_SUCCESS;
+
+ if (g_pPartitionFiles[nPartitionID] != NULL)
+ {
+ /* The partition must not be currently opened */
+ LogError("g_pPartitionFiles not NULL");
+ return S_ERROR_BAD_STATE;
+ }
+
+ /* Try to erase the file */
+#if defined(LINUX) || (defined __ANDROID32__) || defined (__SYMBIAN32__)
+ if (unlink(g_pPartitionNames[nPartitionID]) != 0)
+#endif
+#ifdef WIN32
+ if (_unlink(g_pPartitionNames[nPartitionID]) != 0)
+#endif
+ {
+ /* File in use or OS didn't allow the operation */
+ nError = errno2serror();
+ }
+
+ return nError;
+}
+
+/**
+ * This function executes the CREATE_PARTITION instruction. When successful,
+ * it fills the g_pPartitionFiles[nPartitionID] slot.
+ *
+ * @param nPartitionID: the partition identifier
+ **/
+static TEEC_Result partitionCreate(uint32_t nPartitionID)
+{
+ uint32_t nError = S_SUCCESS;
+
+ if (g_pPartitionFiles[nPartitionID] != NULL)
+ {
+ /* The partition is already opened */
+ LogError("g_pPartitionFiles not NULL");
+ return S_ERROR_BAD_STATE;
+ }
+
+ /* Create the file unconditionnally */
+ LogInfo("Create storage file \"%s\"", g_pPartitionNames[nPartitionID]);
+ g_pPartitionFiles[nPartitionID] = fopen(g_pPartitionNames[nPartitionID], "w+b");
+
+ if (g_pPartitionFiles[nPartitionID] == NULL)
+ {
+ LogError("Cannot create storage file \"%s\"", g_pPartitionNames[nPartitionID]);
+ nError = errno2serror();
+ return nError;
+ }
+
+ return nError;
+}
+
+/**
+ * This function executes the OPEN_PARTITION instruction. When successful,
+ * it fills the g_pPartitionFiles[nPartitionID] slot and writes the partition
+ * size in hResultEncoder
+ *
+ * @param nPartitionID: the partition identifier
+ * @param pnPartitionSize: filled with the number of sectors in the partition
+ **/
+static TEEC_Result partitionOpen(uint32_t nPartitionID, uint32_t* pnPartitionSize)
+{
+ uint32_t nError = S_SUCCESS;
+
+ if (g_pPartitionFiles[nPartitionID] != NULL)
+ {
+ /* No partition must be currently opened in the session */
+ LogError("g_pPartitionFiles not NULL");
+ return S_ERROR_BAD_STATE;
+ }
+
+ /* Open the file */
+ g_pPartitionFiles[nPartitionID] = fopen(g_pPartitionNames[nPartitionID], "r+b");
+ if (g_pPartitionFiles[nPartitionID] == NULL)
+ {
+ if (errno == ENOENT)
+ {
+ /* File does not exist */
+ LogError("Storage file \"%s\" does not exist", g_pPartitionNames[nPartitionID]);
+ nError = S_ERROR_ITEM_NOT_FOUND;
+ return nError;
+ }
+ else
+ {
+ LogError("cannot open storage file \"%s\"", g_pPartitionNames[nPartitionID]);
+ nError = errno2serror();
+ return nError;
+ }
+ }
+ /* Determine the current number of sectors */
+ fseek(g_pPartitionFiles[nPartitionID], 0L, SEEK_END);
+ *pnPartitionSize = ftell(g_pPartitionFiles[nPartitionID]) / g_nSectorSize;
+
+ LogInfo("storage file \"%s\" successfully opened (size = %d KB (%d bytes))",
+ g_pPartitionNames[nPartitionID],
+ ((*pnPartitionSize) * g_nSectorSize) / 1024,
+ ((*pnPartitionSize) * g_nSectorSize));
+
+ return nError;
+}
+
+
+/**
+ * This function executes the CLOSE_PARTITION instruction.
+ * It closes the partition file.
+ *
+ * @param nPartitionID: the partition identifier
+ **/
+static TEEC_Result partitionClose(uint32_t nPartitionID)
+{
+ if (g_pPartitionFiles[nPartitionID] == NULL)
+ {
+ /* The partition is currently not opened */
+ return S_ERROR_BAD_STATE;
+ }
+ fclose(g_pPartitionFiles[nPartitionID]);
+ g_pPartitionFiles[nPartitionID] = NULL;
+ return S_SUCCESS;
+}
+
+/**
+ * This function executes the READ instruction.
+ *
+ * @param nPartitionID: the partition identifier
+ * @param nSectorIndex: the index of the sector to read
+ * @param nWorkspaceOffset: the offset in the workspace where the sector must be written
+ **/
+static TEEC_Result partitionRead(uint32_t nPartitionID, uint32_t nSectorIndex, uint32_t nWorkspaceOffset)
+{
+ FILE* pFile;
+
+ TRACE_INFO(">Partition %1X: read sector 0x%08X into workspace at offset 0x%08X",
+ nPartitionID, nSectorIndex, nWorkspaceOffset);
+
+ pFile = g_pPartitionFiles[nPartitionID];
+
+ if (pFile == NULL)
+ {
+ /* The partition is not opened */
+ return S_ERROR_BAD_STATE;
+ }
+
+ if (fseek(pFile, nSectorIndex*g_nSectorSize, SEEK_SET) != 0)
+ {
+ LogError("fseek error: %s", strerror(errno));
+ return errno2serror();
+ }
+
+ if (fread(g_pWorkspaceBuffer + nWorkspaceOffset,
+ g_nSectorSize, 1,
+ pFile) != 1)
+ {
+ if (feof(pFile))
+ {
+ LogError("fread error: End-Of-File detected");
+ return S_ERROR_ITEM_NOT_FOUND;
+ }
+ LogError("fread error: %s", strerror(errno));
+ return errno2serror();
+ }
+
+ return S_SUCCESS;
+}
+
+/**
+ * This function executes the WRITE instruction.
+ *
+ * @param nPartitionID: the partition identifier
+ * @param nSectorIndex: the index of the sector to read
+ * @param nWorkspaceOffset: the offset in the workspace where the sector must be read
+ **/
+static TEEC_Result partitionWrite(uint32_t nPartitionID, uint32_t nSectorIndex, uint32_t nWorkspaceOffset)
+{
+ FILE* pFile;
+
+ TRACE_INFO(">Partition %1X: write sector 0x%X from workspace at offset 0x%X",
+ nPartitionID, nSectorIndex, nWorkspaceOffset);
+
+ pFile = g_pPartitionFiles[nPartitionID];
+
+ if (pFile == NULL)
+ {
+ /* The partition is not opened */
+ return S_ERROR_BAD_STATE;
+ }
+
+ if (fseek(pFile, nSectorIndex*g_nSectorSize, SEEK_SET) != 0)
+ {
+ LogError("fseek error: %s", strerror(errno));
+ return errno2serror();
+ }
+
+ if (fwrite(g_pWorkspaceBuffer + nWorkspaceOffset,
+ g_nSectorSize, 1,
+ pFile) != 1)
+ {
+ LogError("fread error: %s", strerror(errno));
+ return errno2serror();
+ }
+ return S_SUCCESS;
+}
+
+
+/**
+ * This function executes the SET_SIZE instruction.
+ *
+ * @param nPartitionID: the partition identifier
+ * @param nNewSectorCount: the new sector count
+ **/
+static TEEC_Result partitionSetSize(uint32_t nPartitionID, uint32_t nNewSectorCount)
+{
+ FILE* pFile;
+ uint32_t nCurrentSectorCount;
+
+ pFile = g_pPartitionFiles[nPartitionID];
+
+ if (pFile==NULL)
+ {
+ /* The partition is not opened */
+ return S_ERROR_BAD_STATE;
+ }
+
+ /* Determine the current size of the partition */
+ if (fseek(pFile, 0, SEEK_END) != 0)
+ {
+ LogError("fseek error: %s", strerror(errno));
+ return errno2serror();
+ }
+ nCurrentSectorCount = ftell(pFile) / g_nSectorSize;
+
+ if (nNewSectorCount > nCurrentSectorCount)
+ {
+ uint32_t nAddedBytesCount;
+ /* Enlarge the partition file. Make sure we actually write
+ some non-zero data into the new sectors. Otherwise, some file-system
+ might not really reserve the storage space but use a
+ sparse representation. In this case, a subsequent write instruction
+ could fail due to out-of-space, which we want to avoid. */
+ nAddedBytesCount = (nNewSectorCount-nCurrentSectorCount)*g_nSectorSize;
+ while (nAddedBytesCount)
+ {
+ if (fputc(0xA5, pFile)!=0xA5)
+ {
+ return errno2serror();
+ }
+ nAddedBytesCount--;
+ }
+ }
+ else if (nNewSectorCount < nCurrentSectorCount)
+ {
+ int result = 0;
+ /* Truncate the partition file */
+#if defined(LINUX) || (defined __ANDROID32__)
+ result = ftruncate(fileno(pFile),nNewSectorCount * g_nSectorSize);
+#endif
+#if defined (__SYMBIAN32__)
+ LogError("No truncate available in Symbian C API");
+#endif
+#ifdef WIN32
+ result = _chsize(_fileno(pFile),nNewSectorCount * g_nSectorSize);
+#endif
+ if (result)
+ {
+ return errno2serror();
+ }
+ }
+ return S_SUCCESS;
+}
+
+/**
+ * This function executes the SYNC instruction.
+ *
+ * @param pPartitionID: the partition identifier
+ **/
+static TEEC_Result partitionSync(uint32_t nPartitionID)
+{
+ TEEC_Result nError = S_SUCCESS;
+ int result;
+
+ FILE* pFile = g_pPartitionFiles[nPartitionID];
+
+ if (pFile == NULL)
+ {
+ /* The partition is not currently opened */
+ return S_ERROR_BAD_STATE;
+ }
+
+ /* First make sure that the data in the stdio buffers
+ is flushed to the file descriptor */
+ result=fflush(pFile);
+ if (result)
+ {
+ nError=errno2serror();
+ goto end;
+ }
+ /* Then synchronize the file descriptor with the file-system */
+
+#if defined(LINUX) || (defined __ANDROID32__)
+ result=fdatasync(fileno(pFile));
+#endif
+#if defined (__SYMBIAN32__)
+ result=fsync(fileno(pFile));
+#endif
+#ifdef WIN32
+ result=_commit(_fileno(pFile));
+#endif
+ if (result)
+ {
+ nError=errno2serror();
+ }
+
+end:
+ return nError;
+}
+
+/**
+ * This function executes the NOTIFY instruction.
+ *
+ * @param pMessage the message string
+ * @param nMessageType the type of messages
+ **/
+static void notify(const wchar_t* pMessage, uint32_t nMessageType)
+{
+ switch (nMessageType)
+ {
+ case DELEGATION_NOTIFY_TYPE_ERROR:
+ LogError("%ls", pMessage);
+ break;
+ case DELEGATION_NOTIFY_TYPE_WARNING:
+ LogWarning("%ls", pMessage);
+ break;
+ case DELEGATION_NOTIFY_TYPE_DEBUG:
+ LogInfo("DEBUG: %ls", pMessage);
+ break;
+ case DELEGATION_NOTIFY_TYPE_INFO:
+ default:
+ LogInfo("%ls", pMessage);
+ break;
+ }
+}
+
+/*----------------------------------------------------------------------------
+ * Session main function
+ *----------------------------------------------------------------------------*/
+
+/*
+ * This function runs a session opened on the delegation service. It fetches
+ * instructions and execute them in a loop. It never returns, but may call
+ * exit when instructed to shutdown by the service
+ */
+static int runSession(TEEC_Context* pContext, TEEC_Session* pSession, TEEC_Operation* pOperation)
+{
+ memset(&g_pExchangeBuffer->sAdministrativeData, 0x00, sizeof(g_pExchangeBuffer->sAdministrativeData));
+
+ while (true)
+ {
+ S_RESULT nError;
+ TEEC_Result nTeeError;
+ uint32_t nInstructionsIndex;
+ uint32_t nInstructionsBufferSize = sizeof(g_pExchangeBuffer->sInstructions);
+
+ pOperation->paramTypes = TEEC_PARAM_TYPES(
+ TEEC_MEMREF_PARTIAL_INPUT,
+ TEEC_MEMREF_PARTIAL_OUTPUT,
+ TEEC_MEMREF_PARTIAL_INOUT,
+ TEEC_NONE);
+ pOperation->params[0].memref.parent = &sExchangeSharedMem;
+ pOperation->params[0].memref.offset = offsetof(DELEGATION_EXCHANGE_BUFFER, sAdministrativeData);
+ pOperation->params[0].memref.size = sizeof(g_pExchangeBuffer->sAdministrativeData);
+
+ pOperation->params[1].memref.parent = &sExchangeSharedMem;
+ pOperation->params[1].memref.offset = offsetof(DELEGATION_EXCHANGE_BUFFER, sInstructions);
+ pOperation->params[1].memref.size = sizeof(g_pExchangeBuffer->sInstructions);
+
+ pOperation->params[2].memref.parent = &sExchangeSharedMem;
+ pOperation->params[2].memref.offset = offsetof(DELEGATION_EXCHANGE_BUFFER, sWorkspace);
+ pOperation->params[2].memref.size = g_nWorkspaceSize;
+
+ nTeeError = TEEC_InvokeCommand(pSession,
+ SERVICE_DELEGATION_GET_INSTRUCTIONS, /* commandID */
+ pOperation, /* IN OUT operation */
+ NULL /* OUT errorOrigin, optional */
+ );
+
+ if (nTeeError != TEEC_SUCCESS)
+ {
+ LogError("TEEC_InvokeCommand error: 0x%08X", nTeeError);
+ LogError("Daemon exits");
+ exit(2);
+ }
+
+ if (pOperation->params[1].tmpref.size > nInstructionsBufferSize)
+ {
+ /* Should not happen, probably an error from the service */
+ pOperation->params[1].tmpref.size = 0;
+ }
+
+ /* Reset the operation results */
+ nError = TEEC_SUCCESS;
+ g_pExchangeBuffer->sAdministrativeData.nSyncExecuted = 0;
+ memset(g_pExchangeBuffer->sAdministrativeData.nPartitionErrorStates, 0x00, sizeof(g_pExchangeBuffer->sAdministrativeData.nPartitionErrorStates));
+ memset(g_pExchangeBuffer->sAdministrativeData.nPartitionOpenSizes, 0x00, sizeof(g_pExchangeBuffer->sAdministrativeData.nPartitionOpenSizes));
+
+ /* Execute the instructions */
+ nInstructionsIndex = 0;
+ nInstructionsBufferSize = pOperation->params[1].tmpref.size;
+ while (true)
+ {
+ DELEGATION_INSTRUCTION * pInstruction;
+ uint32_t nInstructionID;
+ pInstruction = (DELEGATION_INSTRUCTION *)(&g_pExchangeBuffer->sInstructions[nInstructionsIndex/4]);
+ if (nInstructionsIndex + 4 <= nInstructionsBufferSize)
+ {
+ nInstructionID = pInstruction->sGeneric.nInstructionID;
+ nInstructionsIndex+=4;
+ }
+ else
+ {
+ goto instruction_parse_end;
+ }
+ if ((nInstructionID & 0x0F) == 0)
+ {
+ /* Partition-independent instruction */
+ switch (nInstructionID)
+ {
+ case DELEGATION_INSTRUCTION_SHUTDOWN:
+ {
+ exit(0);
+ /* The implementation of the TF Client API will automatically
+ destroy the context and release any associated resource */
+ }
+ case DELEGATION_INSTRUCTION_NOTIFY:
+ {
+ /* Parse the instruction parameters */
+ wchar_t pMessage[100];
+ uint32_t nMessageType;
+ uint32_t nMessageSize;
+ memset(pMessage, 0, 100*sizeof(wchar_t));
+
+ if (nInstructionsIndex + 8 <= nInstructionsBufferSize)
+ {
+ nMessageType = pInstruction->sNotify.nMessageType;
+ nMessageSize = pInstruction->sNotify.nMessageSize;
+ nInstructionsIndex+=8;
+ }
+ else
+ {
+ goto instruction_parse_end;
+ }
+ if (nMessageSize > (99)*sizeof(wchar_t))
+ {
+ /* How to handle the error correctly in this case ? */
+ goto instruction_parse_end;
+ }
+ if (nInstructionsIndex + nMessageSize <= nInstructionsBufferSize)
+ {
+ memcpy(pMessage, &pInstruction->sNotify.nMessage[0], nMessageSize);
+ nInstructionsIndex+=nMessageSize;
+ }
+ else
+ {
+ goto instruction_parse_end;
+ }
+ /* Align the pInstructionsIndex on 4 bytes */
+ nInstructionsIndex = (nInstructionsIndex+3)&~3;
+ notify(pMessage, nMessageType);
+ break;
+ }
+ default:
+ LogError("Unknown instruction identifier: %02X", nInstructionID);
+ nError = S_ERROR_BAD_PARAMETERS;
+ break;
+ }
+ }
+ else
+ {
+ /* Partition-specific instruction */
+ uint32_t nPartitionID = (nInstructionID & 0xF0) >> 4;
+ if (g_pExchangeBuffer->sAdministrativeData.nPartitionErrorStates[nPartitionID] == S_SUCCESS)
+ {
+ /* Execute the instruction only if there is currently no
+ error on the partition */
+ switch (nInstructionID & 0x0F)
+ {
+ case DELEGATION_INSTRUCTION_PARTITION_CREATE:
+ nError = partitionCreate(nPartitionID);
+ TRACE_INFO("INSTRUCTION: ID=0x%x pid=%d err=%d", (nInstructionID & 0x0F), nPartitionID, nError);
+ break;
+ case DELEGATION_INSTRUCTION_PARTITION_OPEN:
+ {
+ uint32_t nPartitionSize = 0;
+ nError = partitionOpen(nPartitionID, &nPartitionSize);
+ TRACE_INFO("INSTRUCTION: ID=0x%x pid=%d pSize=%d err=%d", (nInstructionID & 0x0F), nPartitionID, nPartitionSize, nError);
+ if (nError == S_SUCCESS)
+ {
+ g_pExchangeBuffer->sAdministrativeData.nPartitionOpenSizes[nPartitionID] = nPartitionSize;
+ }
+ break;
+ }
+ case DELEGATION_INSTRUCTION_PARTITION_READ:
+ {
+ /* Parse parameters */
+ uint32_t nSectorID;
+ uint32_t nWorkspaceOffset;
+ if (nInstructionsIndex + 8 <= nInstructionsBufferSize)
+ {
+ nSectorID = pInstruction->sReadWrite.nSectorID;
+ nWorkspaceOffset = pInstruction->sReadWrite.nWorkspaceOffset;
+ nInstructionsIndex+=8;
+ }
+ else
+ {
+ goto instruction_parse_end;
+ }
+ nError = partitionRead(nPartitionID, nSectorID, nWorkspaceOffset);
+ TRACE_INFO("INSTRUCTION: ID=0x%x pid=%d sid=%d woff=%d err=%d", (nInstructionID & 0x0F), nPartitionID, nSectorID, nWorkspaceOffset, nError);
+ break;
+ }
+ case DELEGATION_INSTRUCTION_PARTITION_WRITE:
+ {
+ /* Parse parameters */
+ uint32_t nSectorID;
+ uint32_t nWorkspaceOffset;
+ if (nInstructionsIndex + 8 <= nInstructionsBufferSize)
+ {
+ nSectorID = pInstruction->sReadWrite.nSectorID;
+ nWorkspaceOffset = pInstruction->sReadWrite.nWorkspaceOffset;
+ nInstructionsIndex+=8;
+ }
+ else
+ {
+ goto instruction_parse_end;
+ }
+ nError = partitionWrite(nPartitionID, nSectorID, nWorkspaceOffset);
+ TRACE_INFO("INSTRUCTION: ID=0x%x pid=%d sid=%d woff=%d err=%d", (nInstructionID & 0x0F), nPartitionID, nSectorID, nWorkspaceOffset, nError);
+ break;
+ }
+ case DELEGATION_INSTRUCTION_PARTITION_SYNC:
+ nError = partitionSync(nPartitionID);
+ TRACE_INFO("INSTRUCTION: ID=0x%x pid=%d err=%d", (nInstructionID & 0x0F), nPartitionID, nError);
+ if (nError == S_SUCCESS)
+ {
+ g_pExchangeBuffer->sAdministrativeData.nSyncExecuted++;
+ }
+ break;
+ case DELEGATION_INSTRUCTION_PARTITION_SET_SIZE:
+ {
+ uint32_t nNewSize;
+ if (nInstructionsIndex + 4 <= nInstructionsBufferSize)
+ {
+ nNewSize = pInstruction->sSetSize.nNewSize;
+ nInstructionsIndex+=4;
+ }
+ else
+ {
+ goto instruction_parse_end;
+ }
+ nError = partitionSetSize(nPartitionID, nNewSize);
+ TRACE_INFO("INSTRUCTION: ID=0x%x pid=%d nNewSize=%d err=%d", (nInstructionID & 0x0F), nPartitionID, nNewSize, nError);
+ break;
+ }
+ case DELEGATION_INSTRUCTION_PARTITION_CLOSE:
+ nError = partitionClose(nPartitionID);
+ TRACE_INFO("INSTRUCTION: ID=0x%x pid=%d err=%d", (nInstructionID & 0x0F), nPartitionID, nError);
+ break;
+ case DELEGATION_INSTRUCTION_PARTITION_DESTROY:
+ nError = partitionDestroy(nPartitionID);
+ TRACE_INFO("INSTRUCTION: ID=0x%x pid=%d err=%d", (nInstructionID & 0x0F), nPartitionID, nError);
+ break;
+ }
+ g_pExchangeBuffer->sAdministrativeData.nPartitionErrorStates[nPartitionID] = nError;
+ }
+ }
+ }
+instruction_parse_end:
+ memset(pOperation, 0, sizeof(TEEC_Operation));
+ }
+}
+
+/**
+ * This function opens a new session to the delegation service.
+ **/
+static int createSession(TEEC_Context* pContext, TEEC_Session* pSession, TEEC_Operation* pOperation)
+{
+ TEEC_Result nError;
+ uint32_t nExchangeBufferSize;
+
+ memset(pOperation, 0, sizeof(TEEC_Operation));
+ pOperation->paramTypes = TEEC_PARAM_TYPES(
+ TEEC_VALUE_OUTPUT, TEEC_NONE, TEEC_NONE, TEEC_NONE);
+ nError = TEEC_OpenSession(pContext,
+ pSession, /* OUT session */
+ &g_sServiceId, /* destination UUID */
+ TEEC_LOGIN_PRIVILEGED, /* connectionMethod */
+ NULL, /* connectionData */
+ pOperation, /* IN OUT operation */
+ NULL /* OUT errorOrigin, optional */
+ );
+ if (nError != TEEC_SUCCESS)
+ {
+ LogError("Error on TEEC_OpenSession : 0x%x", nError);
+ exit(2);
+ }
+ /* Read sector size */
+ g_nSectorSize = pOperation->params[0].value.a;
+ LogInfo("Sector Size: %d bytes", g_nSectorSize);
+
+ /* Check sector size */
+ if (!(g_nSectorSize == 512 || g_nSectorSize == 1024 || g_nSectorSize == 2048 || g_nSectorSize == 4096))
+ {
+ LogError("Incorrect sector size: terminating...");
+ exit(2);
+ }
+
+ /* Check workspace size */
+ if (g_nWorkspaceSize < 8 * g_nSectorSize)
+ {
+ g_nWorkspaceSize = 8 * g_nSectorSize;
+ LogWarning("Workspace size too small, automatically set to %d bytes", g_nWorkspaceSize);
+ }
+ /* Compute the size of the exchange buffer */
+ nExchangeBufferSize = sizeof(DELEGATION_EXCHANGE_BUFFER)-1+g_nWorkspaceSize;
+ g_pExchangeBuffer = (DELEGATION_EXCHANGE_BUFFER*)malloc(nExchangeBufferSize);
+ if (g_pExchangeBuffer == NULL)
+ {
+ LogError("Cannot allocate exchange buffer of %d bytes", nExchangeBufferSize);
+ LogError("Now exiting...");
+ exit(2);
+ }
+ g_pWorkspaceBuffer = (uint8_t*)g_pExchangeBuffer->sWorkspace;
+ memset(g_pExchangeBuffer, 0x00, nExchangeBufferSize);
+ memset(g_pPartitionFiles,0,16*sizeof(FILE*));
+
+ /* Register the exchange buffer as a shared memory block */
+ sExchangeSharedMem.buffer = g_pExchangeBuffer;
+ sExchangeSharedMem.size = nExchangeBufferSize;
+ sExchangeSharedMem.flags = TEEC_MEM_INPUT | TEEC_MEM_OUTPUT;
+ nError = TEEC_RegisterSharedMemory(pContext, &sExchangeSharedMem);
+ if (nError != TEEC_SUCCESS)
+ {
+ LogError("Error on TEEC_RegisterSharedMemory : 0x%x", nError);
+ free(g_pExchangeBuffer);
+ exit(2);
+ }
+ LogInfo("Daemon now connected");
+ return 0;
+}
+
+
+/*----------------------------------------------------------------------------
+ * Main
+ *----------------------------------------------------------------------------*/
+
+#ifdef INCLUDE_CLIENT_DELEGATION
+int delegation_main(int argc, char* argv[])
+#else
+int main(int argc, char* argv[])
+#endif
+{
+ TEEC_Result nError;
+ TEEC_Context sContext;
+ TEEC_Session sSession;
+ TEEC_Operation sOperation;
+ bool debug = false;
+
+#ifndef SUPPORT_DELEGATION_EXTENSION
+ char * baseDir = NULL;
+
+ LogInfo("TFSW Normal-World Daemon");
+#else
+ LogInfo("TFSW Normal-World Ext Daemon");
+#endif
+ LogInfo(S_VERSION_STRING);
+ LogInfo("");
+
+ /* Skip program name */
+ argv++;
+ argc--;
+
+ while (argc != 0)
+ {
+ if (strcmp(argv[0], "-d") == 0)
+ {
+ debug = true;
+ }
+#ifdef SUPPORT_DELEGATION_EXTENSION
+ else if (strcmp(argv[0], "-c") == 0)
+ {
+ int error;
+ argc--;
+ argv++;
+ if (argc == 0)
+ {
+ printUsage();
+ return 1;
+ }
+ /* Note that the function parseCommandLineExtension can modify the
+ content of the g_partitionNames array */
+ error = parseCommandLineExtension(argv[0], g_pPartitionNames);
+ if ( error != 0 )
+ {
+ printUsage();
+ return error;
+ }
+ }
+#else
+ else if (strcmp(argv[0], "-storageDir") == 0)
+ {
+ uint32_t i = 0;
+ argc--;
+ argv++;
+ if (argc == 0)
+ {
+ printUsage();
+ return 1;
+ }
+ if (baseDir != NULL)
+ {
+ LogError("Only one storage directory may be specified");
+ return 1;
+ }
+ baseDir = malloc(strlen(argv[0])+1); /* Zero-terminated string */
+ if (baseDir == NULL)
+ {
+ LogError("Out of memory");
+ return 2;
+ }
+
+ strcpy(baseDir, argv[0]);
+
+ /* Set default names to the partitions */
+ for ( i=0; i<16 ;i++ )
+ {
+ g_pPartitionNames[i] = NULL;
+ g_pPartitionNames[i] = malloc(strlen(baseDir) + 1 /* separator */ + sizeof("Store_X.tf"));
+ if (g_pPartitionNames[i] != NULL)
+ {
+ sprintf(g_pPartitionNames[i], "%s%cStore_%1X.tf", baseDir, PATH_SEPARATOR, i);
+ }
+ else
+ {
+ free(baseDir);
+ i=0;
+ while(g_pPartitionNames[i] != NULL) free(g_pPartitionNames[i++]);
+ LogError("Out of memory");
+ return 2;
+ }
+ }
+ }
+ else if (strcmp(argv[0], "-workspaceSize") == 0)
+ {
+ argc--;
+ argv++;
+ if (argc == 0)
+ {
+ printUsage();
+ return 1;
+ }
+ g_nWorkspaceSize=atol(argv[0]);
+ }
+#endif /* ! SUPPORT_DELEGATION_EXTENSION */
+ /*****************************************/
+ else if (strcmp(argv[0], "--help") == 0 || strcmp(argv[0], "-h") == 0)
+ {
+ printUsage();
+ return 0;
+ }
+ else
+ {
+ printUsage();
+ return 1;
+ }
+ argc--;
+ argv++;
+ }
+
+#ifndef SUPPORT_DELEGATION_EXTENSION
+ if (baseDir == NULL)
+ {
+ LogError("-storageDir option is mandatory");
+ return 1;
+ }
+ else
+ {
+ if (static_checkStorageDirAndAccessRights(baseDir) != 0)
+ {
+ return 1;
+ }
+ }
+#endif /* #ifndef SUPPORT_DELEGATION_EXTENSION */
+
+ /*
+ * Detach the daemon from the console
+ */
+
+#if defined(LINUX) || (defined __ANDROID32__)
+ {
+ /*
+ * Turns this application into a daemon => fork off parent process, setup logging, ...
+ */
+
+ /* Our process ID and Session ID */
+ pid_t pid, sid;
+
+ if (!debug)
+ {
+ LogInfo("tf_daemon is detaching from console... Further traces go to syslog");
+ /* Fork off the parent process */
+ pid = fork();
+ if (pid < 0)
+ {
+ LogError("daemon forking failed");
+ return 1;
+ }
+
+ if (pid > 0)
+ {
+ /* parent */
+ return 0;
+ }
+ bDetached = true;
+ }
+
+ /* Change the file mode mask */
+ umask(0077);
+
+ if (!debug)
+ {
+ /* Open any logs here */
+ openlog("tf_daemon", 0, LOG_DAEMON);
+
+ /* Detach from the console */
+ sid = setsid();
+ if (sid < 0)
+ {
+ /* Log the failure */
+ LogError("daemon group creation failed");
+ return 1;
+ }
+ /* Close out the standard file descriptors */
+ close(STDIN_FILENO);
+ close(STDOUT_FILENO);
+ close(STDERR_FILENO);
+ }
+ }
+ /* Change priority so that tf_driver.ko with no polling thread is faster */
+ if (setpriority(PRIO_PROCESS, 0, 19)!=0)
+ {
+ LogError("Daemon cannot change priority");
+ return 1;
+ }
+
+#endif
+
+ TRACE_INFO("Sector size is %d", g_nSectorSize);
+
+ LogInfo("tf_daemon - started");
+
+ nError = TEEC_InitializeContext(NULL, /* const char * name */
+ &sContext); /* TEEC_Context* context */
+ if (nError != TEEC_SUCCESS)
+ {
+ LogError("TEEC_InitializeContext error: 0x%08X", nError);
+ LogError("Now exiting...");
+ exit(2);
+ }
+
+ /* Open a session */
+ if(createSession(&sContext, &sSession, &sOperation) == 0)
+ {
+ /* Run the session. This should never return */
+ runSession(&sContext, &sSession, &sOperation);
+ }
+ TEEC_FinalizeContext(&sContext);
+
+ return 3;
+}