blob: 0b2293a006028cb94b1569c6890ee711247c49aa [file] [log] [blame]
/** Mobicore Driver Registry.
*
* Implements the MobiCore driver registry which maintains trustlets.
*
* @file
* @ingroup MCD_MCDIMPL_DAEMON_REG
*/
/*
* Copyright (c) 2013 TRUSTONIC LIMITED
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. 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.
*
* 3. Neither the name of the TRUSTONIC LIMITED 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 HOLDER 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 <stdlib.h>
#include <dirent.h>
#include <stdio.h>
#include <sys/stat.h>
#include <assert.h>
#include <string.h>
#include <string>
#include <cstring>
#include <cstddef>
#include <sys/mman.h>
#include <errno.h>
#include <fcntl.h>
#include <libgen.h>
#include "mcLoadFormat.h"
#include "mcSpid.h"
#include "mcVersionHelper.h"
#include "PrivateRegistry.h"
#include "MobiCoreRegistry.h"
#include "uuid_attestation.h"
#include "log.h"
/** Maximum size of a trustlet in bytes. */
#define MAX_TL_SIZE (1 * 1024 * 1024)
/** Maximum size of a shared object container in bytes. */
#define MAX_SO_CONT_SIZE (512)
// Asserts expression at compile-time (to be used within a function body).
#define ASSERT_STATIC(e) do { enum { assert_static__ = 1 / (e) }; } while (0)
#define MC_REGISTRY_CONTAINER_PATH "/data/app/mcRegistry"
#define MC_REGISTRY_DEFAULT_PATH "/system/app/mcRegistry"
#define MC_REGISTRY_FALLBACK_PATH "/data/app/mcRegistry"
#define AUTH_TOKEN_FILE_NAME "00000000.authtokcont"
#define ENV_MC_AUTH_TOKEN_PATH "MC_AUTH_TOKEN_PATH"
#define ROOT_FILE_NAME "00000000.rootcont"
#define SP_CONT_FILE_EXT ".spcont"
#define TL_CONT_FILE_EXT ".tlcont"
#define DATA_CONT_FILE_EXT ".datacont"
#define TL_BIN_FILE_EXT ".tlbin"
#define GP_TA_BIN_FILE_EXT ".tabin"
#define GP_TA_SPID_FILE_EXT ".spid"
using namespace std;
//------------------------------------------------------------------------------
static string byteArrayToString(const void *bytes, size_t elems)
{
char hx[elems * 2 + 1];
for (size_t i = 0; i < elems; i++) {
sprintf(&hx[i * 2], "%02x", ((uint8_t *)bytes)[i]);
}
return string(hx);
}
//------------------------------------------------------------------------------
static string uint32ToString(uint32_t value)
{
char hx[8 + 1];
snprintf(hx, sizeof(hx), "%08X", value);
string str(hx);
return string(str.rbegin(), str.rend());
}
//------------------------------------------------------------------------------
static bool doesDirExist(const char *path)
{
struct stat ss;
if (path != NULL && stat(path, &ss) == 0 && S_ISDIR(ss.st_mode)) {
return true;
}
return false;
}
//------------------------------------------------------------------------------
static string getRegistryPath()
{
string registryPath;
// use the default registry path.
registryPath = MC_REGISTRY_CONTAINER_PATH;
LOG_I(" Using default registry path %s", registryPath.c_str());
assert(registryPath.length() != 0);
return registryPath;
}
string getTbStoragePath()
{
return getRegistryPath()+"/TbStorage";
}
//------------------------------------------------------------------------------
string getTlRegistryPath()
{
string registryPath;
// First, attempt to use regular registry environment variable.
if (doesDirExist(MC_REGISTRY_DEFAULT_PATH)) {
registryPath = MC_REGISTRY_DEFAULT_PATH;
LOG_I(" Using MC_REGISTRY_PATH %s", registryPath.c_str());
} else if (doesDirExist(MC_REGISTRY_FALLBACK_PATH)) {
// Second, attempt to use fallback registry environment variable.
registryPath = MC_REGISTRY_FALLBACK_PATH;
LOG_I(" Using MC_REGISTRY_FALLBACK_PATH %s", registryPath.c_str());
}
// As a last resort, use the default registry path.
if (registryPath.length() == 0) {
registryPath = MC_REGISTRY_CONTAINER_PATH;
LOG_I(" Using default registry path %s", registryPath.c_str());
}
assert(registryPath.length() != 0);
return registryPath;
}
//------------------------------------------------------------------------------
static string getAuthTokenFilePath()
{
const char *path;
string authTokenPath;
// First, attempt to use regular auth token path environment variable.
path = getenv(ENV_MC_AUTH_TOKEN_PATH);
if (doesDirExist(path)) {
LOG_I("getAuthTokenFilePath(): Using MC_AUTH_TOKEN_PATH %s", path);
authTokenPath = path;
} else {
authTokenPath = getRegistryPath();
LOG_I("getAuthTokenFilePath(): Using path %s", authTokenPath.c_str());
}
return authTokenPath + "/" + AUTH_TOKEN_FILE_NAME;
}
//------------------------------------------------------------------------------
static string getRootContFilePath()
{
return getRegistryPath() + "/" + ROOT_FILE_NAME;
}
//------------------------------------------------------------------------------
static string getSpDataPath(mcSpid_t spid)
{
return getRegistryPath() + "/" + uint32ToString(spid);
}
//------------------------------------------------------------------------------
static string getSpContFilePath(mcSpid_t spid)
{
return getRegistryPath() + "/" + uint32ToString(spid) + SP_CONT_FILE_EXT;
}
//------------------------------------------------------------------------------
static string getTlContFilePath(const mcUuid_t *uuid, const mcSpid_t spid)
{
return getRegistryPath() + "/" + byteArrayToString(uuid, sizeof(*uuid))
+ "." + uint32ToString(spid) + TL_CONT_FILE_EXT;
}
//------------------------------------------------------------------------------
static string getTlDataPath(const mcUuid_t *uuid)
{
return getRegistryPath() + "/" + byteArrayToString(uuid, sizeof(*uuid));
}
//------------------------------------------------------------------------------
static string getTlDataFilePath(const mcUuid_t *uuid, mcPid_t pid)
{
return getTlDataPath(uuid) + "/" + uint32ToString(pid.data) + DATA_CONT_FILE_EXT;
}
//------------------------------------------------------------------------------
static string getTlBinFilePath(const mcUuid_t *uuid)
{
return getTlRegistryPath() + "/" + byteArrayToString(uuid, sizeof(*uuid)) + TL_BIN_FILE_EXT;
}
//------------------------------------------------------------------------------
static string getTABinFilePath(const mcUuid_t *uuid)
{
return getTlRegistryPath() + "/" + byteArrayToString(uuid, sizeof(*uuid)) + GP_TA_BIN_FILE_EXT;
}
//------------------------------------------------------------------------------
static string getTASpidFilePath(const mcUuid_t *uuid)
{
return getTlRegistryPath() + "/" + byteArrayToString(uuid, sizeof(*uuid)) + GP_TA_SPID_FILE_EXT;
}
//------------------------------------------------------------------------------
mcResult_t mcRegistryStoreAuthToken(void *so, uint32_t size)
{
if (so == NULL || size > 3 * MAX_SO_CONT_SIZE) {
LOG_E("mcRegistry store So.Soc failed: %d", MC_DRV_ERR_INVALID_PARAMETER);
return MC_DRV_ERR_INVALID_PARAMETER;
}
const string &authTokenFilePath = getAuthTokenFilePath();
LOG_I("store AuthToken: %s", authTokenFilePath.c_str());
FILE *fs = fopen(authTokenFilePath.c_str(), "wb");
if (!fs) {
LOG_E("mcRegistry store So.Soc failed: %d", MC_DRV_ERR_INVALID_DEVICE_FILE);
return MC_DRV_ERR_INVALID_DEVICE_FILE;
}
fseek(fs, 0, SEEK_SET);
fwrite((char *)so, 1, size, fs);
fflush(fs);
fclose(fs);
return MC_DRV_OK;
}
//------------------------------------------------------------------------------
mcResult_t mcRegistryReadAuthToken(mcSoAuthTokenCont_t *so)
{
if (NULL == so) {
LOG_E("mcRegistry read So.Soc failed: %d", MC_DRV_ERR_INVALID_PARAMETER);
return MC_DRV_ERR_INVALID_PARAMETER;
}
const string &authTokenFilePath = getAuthTokenFilePath();
LOG_I("read AuthToken: %s", authTokenFilePath.c_str());
FILE *fs = fopen(authTokenFilePath.c_str(), "rb");
if (!fs) {
LOG_W("mcRegistry read So.Soc failed: %d", MC_DRV_ERR_INVALID_DEVICE_FILE);
return MC_DRV_ERR_INVALID_DEVICE_FILE;
}
fseek(fs, 0, SEEK_END);
int32_t filesize = ftell(fs);
if (sizeof(mcSoAuthTokenCont_t) != filesize) {
fclose(fs);
LOG_W("mcRegistry read So.Soc failed: %d", MC_DRV_ERR_OUT_OF_RESOURCES);
return MC_DRV_ERR_OUT_OF_RESOURCES;
}
fseek(fs, 0, SEEK_SET);
if (fread((char *)so, 1, sizeof(mcSoAuthTokenCont_t), fs) !=
sizeof(mcSoAuthTokenCont_t))
{
fclose(fs);
LOG_W("mcRegistry read So.Soc failed: %d", MC_DRV_ERR_INVALID_PARAMETER);
return MC_DRV_ERR_INVALID_PARAMETER;
}
fclose(fs);
return MC_DRV_OK;
}
//------------------------------------------------------------------------------
mcResult_t mcRegistryDeleteAuthToken(void)
{
if (remove(getAuthTokenFilePath().c_str())) {
LOG_ERRNO("Delete Auth token file!");
return MC_DRV_ERR_UNKNOWN;
} else
return MC_DRV_OK;
}
//------------------------------------------------------------------------------
mcResult_t mcRegistryStoreRoot(void *so, uint32_t size)
{
if (so == NULL || size > 3 * MAX_SO_CONT_SIZE) {
LOG_E("mcRegistry store So.Root failed: %d", MC_DRV_ERR_INVALID_PARAMETER);
return MC_DRV_ERR_INVALID_PARAMETER;
}
const string &rootContFilePath = getRootContFilePath();
LOG_I("store Root: %s", rootContFilePath.c_str());
FILE *fs = fopen(rootContFilePath.c_str(), "wb");
if (!fs) {
LOG_E("mcRegistry store So.Root failed: %d", MC_DRV_ERR_INVALID_DEVICE_FILE);
return MC_DRV_ERR_INVALID_DEVICE_FILE;
}
fseek(fs, 0, SEEK_SET);
fwrite((char *)so, 1, size, fs);
fflush(fs);
fclose(fs);
return MC_DRV_OK;
}
//------------------------------------------------------------------------------
mcResult_t mcRegistryReadRoot(void *so, uint32_t *size)
{
const string &rootContFilePath = getRootContFilePath();
size_t readBytes;
if (so == NULL) {
LOG_E("mcRegistry read So.Root failed: %d", MC_DRV_ERR_INVALID_PARAMETER);
return MC_DRV_ERR_INVALID_PARAMETER;
}
LOG_I(" Opening %s", rootContFilePath.c_str());
FILE *fs = fopen(rootContFilePath.c_str(), "rb");
if (!fs) {
LOG_W("mcRegistry read So.Root failed: %d", MC_DRV_ERR_INVALID_DEVICE_FILE);
return MC_DRV_ERR_INVALID_DEVICE_FILE;
}
readBytes = fread((char *)so, 1, *size, fs);
fclose(fs);
if (readBytes > 0) {
*size = readBytes;
return MC_DRV_OK;
} else {
LOG_E("mcRegistry read So.Root failed: %d", MC_DRV_ERR_INVALID_DEVICE_FILE);
return MC_DRV_ERR_INVALID_DEVICE_FILE;
}
}
//------------------------------------------------------------------------------
mcResult_t mcRegistryStoreSp(mcSpid_t spid, void *so, uint32_t size)
{
if ((spid == 0) || (so == NULL) || size > 3 * MAX_SO_CONT_SIZE) {
LOG_E("mcRegistry store So.Sp(SpId) failed: %d", MC_DRV_ERR_INVALID_PARAMETER);
return MC_DRV_ERR_INVALID_PARAMETER;
}
const string &spContFilePath = getSpContFilePath(spid);
LOG_I("store SP: %s", spContFilePath.c_str());
FILE *fs = fopen(spContFilePath.c_str(), "wb");
if (!fs) {
LOG_E("mcRegistry store So.Sp(SpId) failed: %d", MC_DRV_ERR_INVALID_DEVICE_FILE);
return MC_DRV_ERR_INVALID_DEVICE_FILE;
}
fseek(fs, 0, SEEK_SET);
fwrite((char *)so, 1, size, fs);
fflush(fs);
fclose(fs);
return MC_DRV_OK;
}
//------------------------------------------------------------------------------
mcResult_t mcRegistryReadSp(mcSpid_t spid, void *so, uint32_t *size)
{
const string &spContFilePath = getSpContFilePath(spid);
size_t readBytes;
if ((spid == 0) || (so == NULL)) {
LOG_E("mcRegistry read So.Sp(SpId=0x%x) failed", spid);
return MC_DRV_ERR_INVALID_PARAMETER;
}
LOG_I(" Reading %s", spContFilePath.c_str());
FILE *fs = fopen(spContFilePath.c_str(), "rb");
if (!fs) {
LOG_E("mcRegistry read So.Sp(SpId) failed: %d", MC_DRV_ERR_INVALID_DEVICE_FILE);
return MC_DRV_ERR_INVALID_DEVICE_FILE;
}
readBytes = fread((char *)so, 1, *size, fs);
fclose(fs);
if (readBytes > 0) {
*size = readBytes;
return MC_DRV_OK;
} else {
LOG_E("mcRegistry read So.Sp(SpId) failed: %d", MC_DRV_ERR_INVALID_DEVICE_FILE);
return MC_DRV_ERR_INVALID_DEVICE_FILE;
}
}
//------------------------------------------------------------------------------
mcResult_t mcRegistryStoreTrustletCon(const mcUuid_t *uuid, const mcSpid_t spid, void *so, uint32_t size)
{
if ((uuid == NULL) || (so == NULL) || size > 3 * MAX_SO_CONT_SIZE) {
LOG_E("mcRegistry store So.TrustletCont(uuid) failed: %d", MC_DRV_ERR_INVALID_PARAMETER);
return MC_DRV_ERR_INVALID_PARAMETER;
}
const string &tlContFilePath = getTlContFilePath(uuid, spid);
LOG_I("store TLc: %s", tlContFilePath.c_str());
FILE *fs = fopen(tlContFilePath.c_str(), "wb");
if (!fs) {
LOG_E("mcRegistry store So.TrustletCont(uuid) failed: %d", MC_DRV_ERR_INVALID_DEVICE_FILE);
return MC_DRV_ERR_INVALID_DEVICE_FILE;
}
fseek(fs, 0, SEEK_SET);
fwrite((char *)so, 1, size, fs);
fflush(fs);
fclose(fs);
return MC_DRV_OK;
}
static uint32_t getAsUint32BE(
void *pValueUnaligned
)
{
uint8_t *p = (uint8_t *)pValueUnaligned;
uint32_t val = p[3] | (p[2] << 8) | (p[1] << 16) | (p[0] << 24);
return val;
}
mcResult_t mcRegistryStoreTABlob(mcSpid_t spid, void *blob, uint32_t size)
{
LOG_I("mcRegistryStoreTABlob started");
// Check blob size
if (size < sizeof(mclfHeaderV24_t)) {
LOG_E("RegistryStoreTABlob failed - TA blob length is less then header size");
return MC_DRV_ERR_INVALID_PARAMETER;
}
mclfHeaderV24_t *header24 = (mclfHeaderV24_t *)blob;
mclfHeaderV2_t *header20 = (mclfHeaderV2_t *)blob;
// Check header version
if (header20->intro.version < MC_MAKE_VERSION(2, 4)) {
LOG_E("RegistryStoreTABlob failed - TA blob header version is less than 2.4");
return MC_DRV_ERR_TA_HEADER_ERROR;
}
//Check GP version
if (header24->gp_level != 1) {
LOG_E("RegistryStoreTABlob failed - TA blob header gp_level is not equal to 1");
return MC_DRV_ERR_TA_HEADER_ERROR;
}
TEEC_UUID uuid;
switch (header20->serviceType) {
case SERVICE_TYPE_SYSTEM_TRUSTLET: {
// Check spid
if (spid != MC_SPID_SYSTEM) {
LOG_E("RegistryStoreTABlob failed - SPID is not equal to %d for System TA", spid);
return MC_DRV_ERR_INVALID_PARAMETER;
}
memcpy(&uuid, &header20->uuid, sizeof(mcUuid_t));
break;
}
case SERVICE_TYPE_SP_TRUSTLET: {
// Check spid
if (spid >= MC_SPID_SYSTEM) {
LOG_E("RegistryStoreTABlob failed - SPID is equal to %u ", spid);
return MC_DRV_ERR_INVALID_PARAMETER;
}
uuid_attestation *pUa = (uuid_attestation *) & ((uint8_t *)blob)[header24->attestationOffset];
// Check attestation size
if ((header24->attestationOffset > size) && (header24->attestationOffset + getAsUint32BE(&pUa->size) > size)) {
LOG_E("RegistryStoreTABlob failed - Attestation size is not correct");
return MC_DRV_ERR_TA_HEADER_ERROR;
}
// Check attestation size
if (getAsUint32BE(&pUa->size) < sizeof(uuid_attestation)) {
LOG_E("RegistryStoreTABlob failed - Attestation size is equal to %d and is less then %d", getAsUint32BE(&pUa->size), sizeof(uuid_attestation));
return MC_DRV_ERR_TA_ATTESTATION_ERROR;
}
// Check magic word
if (memcmp(pUa->magic, MAGIC, AT_MAGIC_SIZE)) {
LOG_E("RegistryStoreTABlob failed - Attestation magic word is not correct");
return MC_DRV_ERR_TA_ATTESTATION_ERROR;
}
// Check version
if (getAsUint32BE(&pUa->version) != AT_VERSION) {
LOG_E("RegistryStoreTABlob failed - Attestation version is equal to %08X. It has to be equal to %08X", getAsUint32BE(&pUa->version), AT_VERSION);
return MC_DRV_ERR_TA_ATTESTATION_ERROR;
}
memcpy(&uuid, &pUa->uuid, sizeof(mcUuid_t));
break;
}
default: {
return MC_DRV_ERR_INVALID_PARAMETER;
}
}
const string tlBinFilePath = getTABinFilePath((mcUuid_t *)&uuid);
LOG_I("Store TA blob at: %s", tlBinFilePath.c_str());
FILE *fs = fopen(tlBinFilePath.c_str(), "wb");
if (!fs) {
LOG_E("RegistryStoreTABlob failed - TA blob file open error: %d", MC_DRV_ERR_INVALID_DEVICE_FILE);
return MC_DRV_ERR_INVALID_DEVICE_FILE;
}
fseek(fs, 0, SEEK_SET);
fwrite(blob, 1, size, fs);
fflush(fs);
fclose(fs);
if (header20->serviceType == SERVICE_TYPE_SP_TRUSTLET) {
const string taspidFilePath = getTASpidFilePath((mcUuid_t *)&uuid);
LOG_I("Store spid file at: %s", taspidFilePath.c_str());
FILE *fs = fopen(taspidFilePath.c_str(), "wb");
if (!fs) {
//TODO: shouldn't we delete TA blob file ?
LOG_E("RegistryStoreTABlob failed - TA blob file open error: %d", MC_DRV_ERR_INVALID_DEVICE_FILE);
return MC_DRV_ERR_INVALID_DEVICE_FILE;
}
fseek(fs, 0, SEEK_SET);
fwrite(&spid, 1, sizeof(mcSpid_t), fs);
fflush(fs);
fclose(fs);
}
return MC_DRV_OK;
}
//------------------------------------------------------------------------------
mcResult_t mcRegistryReadTrustletCon(const mcUuid_t *uuid, const mcSpid_t spid, void *so, uint32_t *size)
{
if ((uuid == NULL) || (so == NULL)) {
LOG_E("mcRegistry read So.TrustletCont(uuid) failed: %d", MC_DRV_ERR_INVALID_PARAMETER);
return MC_DRV_ERR_INVALID_PARAMETER;
}
size_t readBytes;
const string &tlContFilePath = getTlContFilePath(uuid, spid);
LOG_I("read TLc: %s", tlContFilePath.c_str());
FILE *fs = fopen(tlContFilePath.c_str(), "rb");
if (!fs) {
LOG_E("mcRegistry read So.TrustletCont(uuid) failed: %d", MC_DRV_ERR_INVALID_DEVICE_FILE);
return MC_DRV_ERR_INVALID_DEVICE_FILE;
}
fseek(fs, 0, SEEK_SET);
readBytes = fread((char *)so, 1, *size, fs);
fclose(fs);
if (readBytes > 0) {
*size = readBytes;
return MC_DRV_OK;
} else {
LOG_E("mcRegistry read So.TrustletCont(uuid) failed: %d", MC_DRV_ERR_INVALID_DEVICE_FILE);
return MC_DRV_ERR_INVALID_DEVICE_FILE;
}
}
//------------------------------------------------------------------------------
mcResult_t mcRegistryStoreData(void *so, uint32_t size)
{
mcSoDataCont_t *dataCont = (mcSoDataCont_t *)so;
if (dataCont == NULL || size != sizeof(mcSoDataCont_t)) {
LOG_E("mcRegistry store So.Data failed: %d", MC_DRV_ERR_INVALID_PARAMETER);
return MC_DRV_ERR_INVALID_PARAMETER;
}
string pathname, filename;
switch (dataCont->cont.type) {
case CONT_TYPE_SPDATA:
LOG_E("SPDATA not supported");
return MC_DRV_ERR_INVALID_PARAMETER;
break;
case CONT_TYPE_TLDATA:
pathname = getTlDataPath(&dataCont->cont.uuid);
filename = getTlDataFilePath(&dataCont->cont.uuid, dataCont->cont.pid);
break;
default:
LOG_E("mcRegistry store So.Data(cid/pid) failed: %d", MC_DRV_ERR_INVALID_PARAMETER);
return MC_DRV_ERR_INVALID_PARAMETER;
}
if (mkdir(pathname.c_str(), 0777) < 0)
{
LOG_E("mcRegistry store So.Data(cid/pid) failed: %d", MC_DRV_ERR_INVALID_PARAMETER);
return MC_DRV_ERR_INVALID_PARAMETER;
}
LOG_I("store DT: %s", filename.c_str());
FILE *fs = fopen(filename.c_str(), "wb");
if (!fs) {
LOG_E("mcRegistry store So.Data(cid/pid) failed: %d", MC_DRV_ERR_INVALID_DEVICE_FILE);
return MC_DRV_ERR_INVALID_DEVICE_FILE;
}
fseek(fs, 0, SEEK_SET);
fwrite((char *)dataCont, 1, MC_SO_SIZE(dataCont->soHeader.plainLen, dataCont->soHeader.encryptedLen), fs);
fflush(fs);
fclose(fs);
return MC_DRV_OK;
}
//------------------------------------------------------------------------------
mcResult_t mcRegistryReadData(uint32_t context, const mcCid_t *cid, mcPid_t pid,
mcSoDataCont_t *so, uint32_t maxLen)
{
if ((NULL == cid) || (NULL == so)) {
LOG_E("mcRegistry read So.Data failed: %d", MC_DRV_ERR_INVALID_PARAMETER);
return MC_DRV_ERR_INVALID_PARAMETER;
}
string filename;
switch (context) {
case 0:
LOG_E("SPDATA not supported");
return MC_DRV_ERR_INVALID_PARAMETER;
break;
case 1:
filename = getTlDataFilePath(&so->cont.uuid, so->cont.pid);
break;
default:
LOG_E("mcRegistry read So.Data(cid/pid) failed: %d", MC_DRV_ERR_INVALID_PARAMETER);
return MC_DRV_ERR_INVALID_PARAMETER;
}
LOG_I("read DT: %s", filename.c_str());
FILE *fs = fopen(filename.c_str(), "rb");
if (!fs) {
LOG_E("mcRegistry read So.Data(cid/pid) failed: %d", MC_DRV_ERR_INVALID_DEVICE_FILE);
return MC_DRV_ERR_INVALID_DEVICE_FILE;
}
fseek(fs, 0, SEEK_END);
uint32_t filesize = ftell(fs);
if (maxLen < filesize) {
fclose(fs);
LOG_E("mcRegistry read So.Data(cid/pid) failed: %d", MC_DRV_ERR_OUT_OF_RESOURCES);
return MC_DRV_ERR_OUT_OF_RESOURCES;
}
fseek(fs, 0, SEEK_SET);
char *p = (char *) so;
if (fread(p, 1, sizeof(mcSoHeader_t), fs) != sizeof(mcSoHeader_t))
{
fclose(fs);
LOG_E("mcRegistry read So.Data(cid/pid) failed: %d", MC_DRV_ERR_INVALID_PARAMETER);
return MC_DRV_ERR_INVALID_PARAMETER;
}
p += sizeof(mcSoHeader_t);
if (fread(p, 1, MC_SO_SIZE(so->soHeader.plainLen,
so->soHeader.encryptedLen)
- sizeof(mcSoHeader_t), fs) !=
MC_SO_SIZE(so->soHeader.plainLen, so->soHeader.encryptedLen)
- sizeof(mcSoHeader_t))
{
fclose(fs);
LOG_E("mcRegistry read So.Data(cid/pid) failed: %d", MC_DRV_ERR_INVALID_PARAMETER);
return MC_DRV_ERR_INVALID_PARAMETER;
}
fclose(fs);
return MC_DRV_OK;
}
//------------------------------------------------------------------------------
static size_t getFileContent(
const char *pPath,
uint8_t **ppContent)
{
FILE *pStream;
long filesize;
uint8_t *content = NULL;
/* Open the file */
pStream = fopen(pPath, "rb");
if (pStream == NULL) {
LOG_E("Error: Cannot open file: %s.", pPath);
return 0;
}
if (fseek(pStream, 0L, SEEK_END) != 0) {
LOG_E("Error: Cannot read file: %s.", pPath);
goto error;
}
filesize = ftell(pStream);
if (filesize < 0) {
LOG_E("Error: Cannot get the file size: %s.", pPath);
goto error;
}
if (filesize == 0) {
LOG_E("Error: Empty file: %s.", pPath);
goto error;
}
/* Set the file pointer at the beginning of the file */
if (fseek(pStream, 0L, SEEK_SET) != 0) {
LOG_E("Error: Cannot read file: %s.", pPath);
goto error;
}
/* Allocate a buffer for the content */
content = (uint8_t *)malloc(filesize);
if (content == NULL) {
LOG_E("Error: Cannot read file: Out of memory.");
goto error;
}
/* Read data from the file into the buffer */
if (fread(content, (size_t)filesize, 1, pStream) != 1) {
LOG_E("Error: Cannot read file: %s.", pPath);
goto error;
}
/* Close the file */
fclose(pStream);
*ppContent = content;
/* Return number of bytes read */
return (size_t)filesize;
error:
if (content != NULL) {
free(content);
}
fclose(pStream);
return 0;
}
//------------------------------------------------------------------------------
static bool mcCheckUuid(const mcUuid_t *uuid, const char *filename)
{
uint8_t *pTAData = NULL;
uint32_t nTASize;
bool res;
nTASize = getFileContent(filename, &pTAData);
if (nTASize == 0) {
LOG_E("err: Trusted Application not found.");
return false;
}
// Check blob size
if (nTASize < sizeof(mclfHeaderV24_t)) {
free(pTAData);
LOG_E("RegistryStoreTABlob failed - TA blob length is less then header size");
return false;
}
mclfHeaderV2_t *header20 = (mclfHeaderV2_t *)pTAData;
// Check header version
if (header20->intro.version < MC_MAKE_VERSION(2, 4)) {
free(pTAData);
LOG_E("RegistryStoreTABlob failed - TA blob header version is less than 2.4");
return false;
}
// Check blob size
if (memcmp(uuid, &header20->uuid, sizeof(mcUuid_t)) == 0) {
res = true;
} else {
res = false;
}
free(pTAData);
return res;
}
//this function deletes all the files owned by a GP TA and stored in the tbase secure storage dir.
//then it deletes GP TA folder.
static int CleanupGPTAStorage(const char *basename)
{
DIR *dp;
struct dirent *de;
int e;
string TAPath = getTlRegistryPath()+"/TbStorage/"+ basename;
if (NULL != (dp = opendir(TAPath.c_str()))) {
while (NULL != (de = readdir(dp))) {
if (de->d_name[0] != '.') {
string dname = TAPath + "/" + string (de->d_name);
LOG_I("delete DT: %s", dname.c_str());
if (0 != (e = remove(dname.c_str()))) {
LOG_E("remove UUID-files %s failed! error: %d", dname.c_str(), e);
}
}
}
if (dp) {
closedir(dp);
}
LOG_I("delete dir: %s", TAPath.c_str());
if (0 != (e = rmdir(TAPath.c_str()))) {
LOG_E("remove UUID-dir failed! errno: %d", e);
return e;
}
}
return MC_DRV_OK;
}
static void deleteSPTA(const mcUuid_t *uuid, const mcSpid_t spid, bool checkUuid)
{
DIR *dp;
struct dirent *de;
int e;
// Delete TABIN and SPID files - we loop searching required spid file
string pathname = getRegistryPath();
if (NULL != (dp = opendir(pathname.c_str()))) {
while (NULL != (de = readdir(dp))) {
string spidFile;
string tabinFile;
string tabinUuid;
size_t pch_dot, pch_slash;
spidFile = pathname + '/' + string(de->d_name);
pch_dot = spidFile.find_last_of('.');
if (pch_dot == string::npos) continue;
pch_slash = spidFile.find_last_of('/');
if ((pch_slash != string::npos) && (pch_slash > pch_dot)) continue;
if (spidFile.substr(pch_dot).compare(GP_TA_SPID_FILE_EXT) != 0) continue;
mcSpid_t curSpid = 0;
int fd = open(spidFile.c_str(), O_RDONLY);
if (fd != -1) {
if (read(fd, &curSpid, sizeof(mcSpid_t))!=sizeof(mcSpid_t)) {
curSpid = 0;
}
close(fd);
}
if (spid == curSpid) {
tabinFile = spidFile.substr(0, pch_dot) + GP_TA_BIN_FILE_EXT;
if ((!checkUuid)||(mcCheckUuid(uuid, tabinFile.c_str()))) {
tabinUuid = spidFile.substr(0, pch_dot);
if (0 != (e = CleanupGPTAStorage(tabinUuid.c_str()))){
LOG_E("cleanup TA Storage dir failed! errno: %d", e);
//return MC_DRV_ERR_UNKNOWN;
}
if (0 != (e = remove(tabinFile.c_str()))) {
LOG_E("remove TA file failed! errno: %d", e);
//return MC_DRV_ERR_UNKNOWN;
}
if (0 != (e = remove(spidFile.c_str()))) {
LOG_E("remove SPID file failed! errno: %d", e);
//return MC_DRV_ERR_UNKNOWN;
}
if (checkUuid) break;
}
}
}
if (dp) {
closedir(dp);
}
}
}
//------------------------------------------------------------------------------
mcResult_t mcRegistryCleanupTrustlet(const mcUuid_t *uuid, const mcSpid_t spid)
{
DIR *dp;
struct dirent *de;
int e;
if (NULL == uuid) {
LOG_E("mcRegistry cleanupTrustlet(uuid) failed: %d", MC_DRV_ERR_INVALID_PARAMETER);
return MC_DRV_ERR_INVALID_PARAMETER;
}
// Delete all TA related data
string pathname = getTlDataPath(uuid);
if (NULL != (dp = opendir(pathname.c_str()))) {
while (NULL != (de = readdir(dp))) {
if (de->d_name[0] != '.') {
string dname = pathname + "/" + string (de->d_name);
LOG_I("delete DT: %s", dname.c_str());
if (0 != (e = remove(dname.c_str()))) {
LOG_E("remove UUID-data %s failed! error: %d", dname.c_str(), e);
}
}
}
if (dp) {
closedir(dp);
}
LOG_I("delete dir: %s", pathname.c_str());
if (0 != (e = rmdir(pathname.c_str()))) {
LOG_E("remove UUID-dir failed! errno: %d", e);
return MC_DRV_ERR_UNKNOWN;
}
}
// Delete TA binary with the name uuid.tlbin
string tlBinFilePath = getTlBinFilePath(uuid);
LOG_I("delete Tlb: %s", tlBinFilePath.c_str());
if (0 != (e = remove(tlBinFilePath.c_str()))) {
LOG_E("remove Tlb failed! errno: %d", e);
// return MC_DRV_ERR_UNKNOWN; // a trustlet-binary must not be present ! (registered but not usable)
}
// Delete TABIN and SPID files - we loop searching required spid file
deleteSPTA(uuid,spid,true);
string tlContFilePath = getTlContFilePath(uuid, spid);
LOG_I("delete Tlc: %s", tlContFilePath.c_str());
if (0 != (e = remove(tlContFilePath.c_str()))) {
LOG_E("remove Tlc failed! errno: %d", e);
return MC_DRV_ERR_UNKNOWN;
}
return MC_DRV_OK;
}
//------------------------------------------------------------------------------
mcResult_t mcRegistryCleanupSp(mcSpid_t spid)
{
DIR *dp;
struct dirent *de;
mcResult_t ret;
mcSoSpCont_t data;
uint32_t i, len;
int e;
if (0 == spid) {
LOG_E("mcRegistry cleanupSP(SpId) failed: %d", MC_DRV_ERR_INVALID_PARAMETER);
return MC_DRV_ERR_INVALID_PARAMETER;
}
len = sizeof(mcSoSpCont_t);
ret = mcRegistryReadSp(spid, &data, &len);
if (MC_DRV_OK != ret || len != sizeof(mcSoSpCont_t)) {
LOG_E("read SP->UUID aborted! Return code: %d", ret);
return ret;
}
for (i = 0; (i < MC_CONT_CHILDREN_COUNT) && (ret == MC_DRV_OK); i++) {
if (0 != strncmp((const char *) & (data.cont.children[i]), (const char *)&MC_UUID_FREE, sizeof(mcUuid_t))) {
ret = mcRegistryCleanupTrustlet(&(data.cont.children[i]), spid);
}
}
if (MC_DRV_OK != ret) {
LOG_E("delete SP->UUID failed! Return code: %d", ret);
return ret;
}
// Delete remaining TABIN and SPID files
deleteSPTA(NULL,spid,false);
string pathname = getSpDataPath(spid);
if (NULL != (dp = opendir(pathname.c_str()))) {
while (NULL != (de = readdir(dp))) {
if (de->d_name[0] != '.') {
string dname = pathname + "/" + string (de->d_name);
LOG_I("delete DT: %s", dname.c_str());
if (0 != (e = remove(dname.c_str()))) {
LOG_E("remove SPID-data %s failed! error: %d", dname.c_str(), e);
}
}
}
if (dp) {
closedir(dp);
}
LOG_I("delete dir: %s", pathname.c_str());
if (0 != (e = rmdir(pathname.c_str()))) {
LOG_E("remove SPID-dir failed! error: %d", e);
return MC_DRV_ERR_UNKNOWN;
}
}
string spContFilePath = getSpContFilePath(spid);
LOG_I("delete Sp: %s", spContFilePath.c_str());
if (0 != (e = remove(spContFilePath.c_str()))) {
LOG_E("remove SP failed! error: %d", e);
return MC_DRV_ERR_UNKNOWN;
}
return MC_DRV_OK;
}
//------------------------------------------------------------------------------
mcResult_t mcRegistryCleanupRoot(void)
{
mcResult_t ret;
mcSoRootCont_t data;
uint32_t i, len;
int e;
len = sizeof(mcSoRootCont_t);
ret = mcRegistryReadRoot(&data, &len);
if (MC_DRV_OK != ret || len != sizeof(mcSoRootCont_t)) {
LOG_E("read Root aborted! Return code: %d", ret);
return ret;
}
for (i = 0; (i < MC_CONT_CHILDREN_COUNT) && (ret == MC_DRV_OK); i++) {
mcSpid_t spid = data.cont.children[i];
if (spid != MC_SPID_FREE) {
ret = mcRegistryCleanupSp(spid);
if (MC_DRV_OK != ret) {
LOG_E("Cleanup SP failed! Return code: %d", ret);
return ret;
}
}
}
string rootContFilePath = getRootContFilePath();
LOG_I("Delete root: %s", rootContFilePath.c_str());
if (0 != (e = remove(rootContFilePath.c_str()))) {
LOG_E("Delete root failed! error: %d", e);
return MC_DRV_ERR_UNKNOWN;
}
return MC_DRV_OK;
}
//------------------------------------------------------------------------------
regObject_t *mcRegistryMemGetServiceBlob(mcSpid_t spid, void *trustlet, uint32_t tlSize)
{
regObject_t *regobj = NULL;
// Ensure that a UUID is provided.
if (NULL == trustlet) {
LOG_E("No trustlet buffer given");
return NULL;
}
// Check service blob size.
if (tlSize > MAX_TL_SIZE ) {
LOG_E("mcRegistryMemGetServiceBlob() failed: service blob too big: %d", tlSize);
return NULL;
}
mclfIntro_t *pIntro = (mclfIntro_t *)trustlet;
// Check TL magic value.
if (pIntro->magic != MC_SERVICE_HEADER_MAGIC_BE) {
LOG_E("mcRegistryMemGetServiceBlob() failed: wrong header magic value: %d", pIntro->magic);
return NULL;
}
// Get service type.
mclfHeaderV2_t *pHeader = (mclfHeaderV2_t *)trustlet;
#ifndef NDEBUG
{
const char *service_types[] = {
"illegal", "Driver", "Trustlet", "System Trustlet"
};
int serviceType_safe = pHeader->serviceType > SERVICE_TYPE_SYSTEM_TRUSTLET ? SERVICE_TYPE_ILLEGAL : pHeader->serviceType;
LOG_I(" Service is a %s (service type %d)", service_types[serviceType_safe], pHeader->serviceType);
}
#endif
LOG_I(" Trustlet text %u data %u ", pHeader->text.len, pHeader->data.len);
// If loadable driver or system trustlet.
if (pHeader->serviceType == SERVICE_TYPE_DRIVER || pHeader->serviceType == SERVICE_TYPE_SYSTEM_TRUSTLET) {
// Take trustlet blob 'as is'.
if (NULL == (regobj = (regObject_t *) (malloc(sizeof(regObject_t) + tlSize)))) {
LOG_E("mcRegistryMemGetServiceBlob() failed: Out of memory");
return NULL;
}
regobj->len = tlSize;
regobj->tlStartOffset = 0;
memcpy((char *)regobj->value, trustlet, tlSize);
// If user trustlet.
} else if (pHeader->serviceType == SERVICE_TYPE_SP_TRUSTLET) {
// Take trustlet blob and append root, sp, and tl container.
size_t regObjValueSize = tlSize + sizeof(mcBlobLenInfo_t) + 3 * MAX_SO_CONT_SIZE;
// Prepare registry object.
if (NULL == (regobj = (regObject_t *) malloc(sizeof(regObject_t) + regObjValueSize))) {
LOG_E("mcRegistryMemGetServiceBlob() failed: Out of memory");
return NULL;
}
regobj->len = regObjValueSize;
regobj->tlStartOffset = sizeof(mcBlobLenInfo_t);
uint8_t *p = regobj->value;
// Reserve space for the blob length structure
mcBlobLenInfo_ptr lenInfo = (mcBlobLenInfo_ptr)p;
lenInfo->magic = MC_TLBLOBLEN_MAGIC;
p += sizeof(mcBlobLenInfo_t);
// Fill in trustlet blob after the len info
memcpy(p, trustlet, tlSize);
p += tlSize;
// Final registry object value looks like this:
//
// +---------------+---------------------------+-----------+---------+---------+
// | Blob Len Info | TL-Header TL-Code TL-Data | Root Cont | SP Cont | TL Cont |
// +---------------+---------------------------+-----------+-------------------+
// /------ Trustlet BLOB ------/
//
// /------------------ regobj->header.len -------------------------------------/
// start at the end of the trustlet blob
mcResult_t ret;
do {
uint32_t soTltContSize = MAX_SO_CONT_SIZE;
uint32_t len;
// Fill in root container.
len = sizeof(mcSoRootCont_t);
if (MC_DRV_OK != (ret = mcRegistryReadRoot(p, &len))) {
break;
}
lenInfo->rootContBlobSize = len;
p += len;
// Fill in SP container.
len = sizeof(mcSoSpCont_t);
if (MC_DRV_OK != (ret = mcRegistryReadSp(spid, p, &len))) {
break;
}
lenInfo->spContBlobSize = len;
p += len;
// Fill in TLT Container
// We know exactly how much space is left in the buffer
soTltContSize = regObjValueSize - tlSize + sizeof(mcBlobLenInfo_t)
- lenInfo->spContBlobSize - lenInfo->rootContBlobSize;
if (MC_DRV_OK != (ret = mcRegistryReadTrustletCon(&pHeader->uuid, spid, p, &soTltContSize))) {
break;
}
lenInfo->tlContBlobSize = soTltContSize;
LOG_I(" Trustlet container %u bytes loaded", soTltContSize);
// Depending on the trustlet container size we decide which structure to use
// Unfortunate design but it should have to do for now
if (soTltContSize == sizeof(mcSoTltCont_2_0_t)) {
LOG_I(" Using 2.0 trustlet container");
} else if (soTltContSize == sizeof(mcSoTltCont_2_1_t)) {
LOG_I(" Using 2.1 trustlet container");
} else {
LOG_E("Trustlet container has unknown size");
break;
}
} while (false);
if (MC_DRV_OK != ret) {
LOG_E("mcRegistryMemGetServiceBlob() failed: Error code: %d", ret);
free(regobj);
return NULL;
}
// Now we know the sizes for all containers so set the correct size
regobj->len = sizeof(mcBlobLenInfo_t) + tlSize +
lenInfo->rootContBlobSize +
lenInfo->spContBlobSize +
lenInfo->tlContBlobSize;
// Any other service type.
} else {
LOG_E("mcRegistryMemGetServiceBlob() failed: Unsupported service type %u", pHeader->serviceType);
}
return regobj;
}
//------------------------------------------------------------------------------
regObject_t *mcRegistryFileGetServiceBlob(const char *trustlet, mcSpid_t spid)
{
struct stat sb;
regObject_t *regobj = NULL;
void *buffer;
// Ensure that a file name is provided.
if (trustlet == NULL) {
LOG_E("No file given");
return NULL;
}
int fd = open(trustlet, O_RDONLY);
if (fd == -1) {
LOG_E("Cannot open %s", trustlet);
return NULL;
}
if (fstat(fd, &sb) == -1) {
LOG_E("mcRegistryFileGetServiceBlob() failed: Cound't get file size");
goto error;
}
buffer = mmap(NULL, sb.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
if (buffer == MAP_FAILED) {
LOG_E("mcRegistryFileGetServiceBlob(): Failed to map file to memory");
goto error;
}
regobj = mcRegistryMemGetServiceBlob(spid, buffer, sb.st_size);
// We don't actually care if either of them fails but should still print warnings
if (munmap(buffer, sb.st_size)) {
LOG_E("mcRegistryFileGetServiceBlob(): Failed to unmap memory");
}
error:
if (close(fd)) {
LOG_E("mcRegistryFileGetServiceBlob(): Failed to close file %s", trustlet);
}
return regobj;
}
//------------------------------------------------------------------------------
regObject_t *mcRegistryGetServiceBlob(const mcUuid_t *uuid, bool isGpUuid)
{
// Ensure that a UUID is provided.
if (NULL == uuid) {
LOG_E("No UUID given");
return NULL;
}
// Open service blob file.
string tlBinFilePath;
if (isGpUuid) {
tlBinFilePath = getTABinFilePath(uuid);
} else {
tlBinFilePath = getTlBinFilePath(uuid);
}
LOG_I("Loading %s", tlBinFilePath.c_str());
mcSpid_t spid = 0;
if (isGpUuid) {
string taspidFilePath = getTASpidFilePath(uuid);
int fd = open(taspidFilePath.c_str(), O_RDONLY);
if (fd == -1) {
// This can be ok for System TAs
//LOG_ERRNO("open");
//LOG_E("Cannot open %s", taspidFilePath.c_str());
//return NULL;
} else {
if (read(fd, &spid, sizeof(mcSpid_t))!=sizeof(mcSpid_t)) {
close(fd);
return NULL;
}
close(fd);
}
}
return mcRegistryFileGetServiceBlob(tlBinFilePath.c_str(), spid);
}
//------------------------------------------------------------------------------
regObject_t *mcRegistryGetDriverBlob(const char *filename)
{
regObject_t *regobj = mcRegistryFileGetServiceBlob(filename, 0);
if (regobj == NULL) {
LOG_E("mcRegistryGetDriverBlob() failed");
return NULL;
}
// Get service type.
mclfHeaderV2_t *pHeader = (mclfHeaderV2_t *)regobj->value;
// If file is not a driver we are not interested
if (pHeader->serviceType != SERVICE_TYPE_DRIVER) {
LOG_E("mcRegistryGetDriverBlob() failed: Unsupported service type %u", pHeader->serviceType);
pHeader = NULL;
free(regobj);
regobj = NULL;
}
return regobj;
}
/** @} */