* Copyright (c) Huawei Technologies Co., Ltd. 2013-2023. All rights reserved.
* Licensed under the Mulan PSL v2.
* You can use this software according to the terms and conditions of the Mulan PSL v2.
* You may obtain a copy of Mulan PSL v2 at:
* http://license.coscl.org.cn/MulanPSL2
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR
* PURPOSE.
* See the Mulan PSL v2 for more details.
*/
#include "tee_client_app_load.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <linux/limits.h>
#include "tee_log.h"
#include "tee_client_api.h"
#include "tc_ns_client.h"
#include "securec.h"
#include "tee_client_inner.h"
#include "tee_load_sec_file.h"
#ifdef LOG_TAG
#undef LOG_TAG
#endif
#define LOG_TAG "teec_app_load"
#define H_OFFSET 32
#define MAX_PATH_LEN 256
static int32_t TEEC_ReadApp(const TaFileInfo *taFile, const char *loadFile, bool defaultPath,
TC_NS_ClientContext *cliContext);
int32_t TEEC_GetApp(const TaFileInfo *taFile, const TEEC_UUID *srvUuid, TC_NS_ClientContext *cliContext)
{
int32_t ret;
char fileName[MAX_FILE_NAME_LEN] = { 0 };
char tempName[MAX_FILE_PATH_LEN + MAX_FILE_NAME_LEN + MAX_FILE_EXT_LEN] = { 0 };
if ((taFile == NULL) || (srvUuid == NULL) || (cliContext == NULL)) {
tloge("param is null\n");
return -1;
}
bool condition = (taFile->taPath != NULL) && (strlen((const char *)taFile->taPath) < MAX_PATH_LEN) &&
strstr((const char *)taFile->taPath, ".sec");
if (condition) {
ret = TEEC_ReadApp(taFile, (const char *)taFile->taPath, false, cliContext);
if (ret < 0) {
tloge("teec load app error, ta path is not NULL\n");
}
} else {
const char *filePath = DYNAMIC_TA_PATH;
ret = snprintf_s(fileName, sizeof(fileName), sizeof(fileName) - 1,
"%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x", srvUuid->timeLow, srvUuid->timeMid,
srvUuid->timeHiAndVersion, srvUuid->clockSeqAndNode[0], srvUuid->clockSeqAndNode[1],
srvUuid->clockSeqAndNode[2], srvUuid->clockSeqAndNode[3], srvUuid->clockSeqAndNode[4],
srvUuid->clockSeqAndNode[5], srvUuid->clockSeqAndNode[6], srvUuid->clockSeqAndNode[7]);
if (ret < 0) {
tloge("get file name err\n");
return -1;
}
size_t filePathLen = strnlen(filePath, MAX_FILE_PATH_LEN);
filePathLen = filePathLen + strnlen(fileName, MAX_FILE_NAME_LEN);
filePathLen = filePathLen + MAX_FILE_EXT_LEN;
int32_t len = snprintf_s(tempName, sizeof(tempName), filePathLen, "%s/%s.sec", filePath, fileName);
if (len < 0) {
tloge("file path too long\n");
return -1;
}
ret = TEEC_ReadApp(taFile, (const char *)tempName, true, cliContext);
if (ret < 0) {
tloge("teec load app error\n");
}
}
return ret;
}
static int32_t GetTaVersion(FILE *fp, uint32_t *taHeadLen, uint32_t *version,
uint32_t *contextLen, uint32_t *totalImgLen)
{
int32_t ret;
TaImageHdrV3 imgIdentity = { { 0 }, 0, 0 };
if (fp == NULL) {
tloge("invalid fp\n");
return -1;
}
ret = (int32_t)fread(&imgIdentity, sizeof(imgIdentity), 1, fp);
if (ret != 1) {
tloge("read file failed, ret=%d, error=%d\n", ret, ferror(fp));
return -1;
}
bool condition = (imgIdentity.img_identity.magic_num1 == TA_HEAD_MAGIC1) &&
(imgIdentity.img_identity.magic_num2 == TA_HEAD_MAGIC2) &&
(imgIdentity.img_identity.version_num > 1);
if (condition) {
tlogd("new version ta\n");
*taHeadLen = sizeof(TeecTaHead);
*version = imgIdentity.img_identity.version_num;
if (*version >= CIPHER_LAYER_VERSION) {
*contextLen = imgIdentity.context_len;
*totalImgLen = *contextLen + (uint32_t)sizeof(imgIdentity);
} else {
ret = fseek(fp, sizeof(imgIdentity.img_identity), SEEK_SET);
if (ret != 0) {
tloge("fseek error\n");
return -1;
}
}
} else {
tlogd("old version ta\n");
*taHeadLen = sizeof(TeecImageHead);
ret = fseek(fp, 0, SEEK_SET);
if (ret != 0) {
tloge("fseek error\n");
return -1;
}
}
return 0;
}
static int32_t TEEC_GetImageLenth(FILE *fp, uint32_t *imgLen)
{
int32_t ret;
TeecImageHead imageHead = { 0 };
uint32_t totalImgLen;
uint32_t taHeadLen = 0;
uint32_t readSize;
uint32_t version = 0;
uint32_t contextLen = 0;
ret = GetTaVersion(fp, &taHeadLen, &version, &contextLen, &totalImgLen);
if (ret != 0) {
tloge("get Ta version failed\n");
return ret;
}
if (version >= CIPHER_LAYER_VERSION) {
goto CHECK_LENTH;
}
readSize = (uint32_t)fread(&imageHead, sizeof(TeecImageHead), 1, fp);
if (readSize != 1) {
tloge("read file failed, err=%u\n", readSize);
return -1;
}
contextLen = imageHead.context_len;
totalImgLen = contextLen + taHeadLen;
CHECK_LENTH:
if ((contextLen > MAX_IMAGE_LEN) || (totalImgLen > MAX_IMAGE_LEN)) {
tloge("check img size failed\n");
return -1;
}
ret = fseek(fp, 0, SEEK_SET);
if (ret != 0) {
tloge("fseek error\n");
return -1;
}
*imgLen = totalImgLen;
return 0;
}
static int32_t TEEC_DoReadApp(FILE *fp, TC_NS_ClientContext *cliContext)
{
uint32_t totalImgLen = 0;
int32_t ret = TEEC_GetImageLenth(fp, &totalImgLen);
if (ret != 0) {
tloge("get image length fail\n");
return -1;
}
char *fileBuffer = malloc(totalImgLen);
if (fileBuffer == NULL) {
tloge("alloc TA file buffer(size=%u) failed\n", totalImgLen);
return -1;
}
uint32_t readSize = (uint32_t)fread(fileBuffer, 1, totalImgLen, fp);
if (readSize != totalImgLen) {
tloge("read ta file failed, read size/total size=%u/%u\n", readSize, totalImgLen);
free(fileBuffer);
return -1;
}
cliContext->file_size = totalImgLen;
cliContext->memref.file_addr = (uint32_t)(uintptr_t)fileBuffer;
cliContext->memref.file_h_addr = (uint32_t)(((uint64_t)(uintptr_t)fileBuffer) >> H_OFFSET);
return 0;
}
static bool TEEC_CheckRealPathSuffix(const char *realPath)
{
const char* realPathSuffix = strrchr(realPath, '.');
if (realPathSuffix == NULL || strnlen(realPathSuffix, PATH_MAX) != strlen(".sec") ||
strncmp(realPathSuffix, ".sec", strlen(".sec")) != 0) {
tloge("realpath suffix -%s- format is wrong\n", realPathSuffix);
return false;
}
return true;
}
static int32_t TEEC_ReadApp(const TaFileInfo *taFile, const char *loadFile, bool defaultPath,
TC_NS_ClientContext *cliContext)
{
int32_t ret = 0;
FILE *fp = NULL;
FILE *fpTmp = NULL;
char realLoadFile[PATH_MAX + 1] = { 0 };
if (taFile->taFp != NULL) {
fp = taFile->taFp;
tlogd("libteec_vendor-read_app: get fp from ta fp\n");
goto READ_APP;
}
if (realpath(loadFile, realLoadFile) == NULL) {
if (defaultPath == false) {
tloge("get file realpath error%d\n", errno);
return -1;
}
tlogd("maybe it's a built-in TA or file is not in default path\n");
return ret;
}
if (!TEEC_CheckRealPathSuffix(realLoadFile)) {
return -1;
}
fpTmp = fopen(realLoadFile, "r");
if (fpTmp == NULL) {
tloge("open file error%d\n", errno);
return -1;
}
fp = fpTmp;
READ_APP:
ret = TEEC_DoReadApp(fp, cliContext);
if (ret != 0) {
tloge("do read app fail\n");
}
if (fpTmp != NULL) {
fclose(fpTmp);
}
return ret;
}
int32_t TEEC_LoadSecfile(const char *filePath, int tzFd, FILE *fp)
{
int ret;
FILE *fpUsable = NULL;
bool checkValue = (tzFd < 0 || filePath == NULL);
FILE *fpCur = NULL;
char realPath[PATH_MAX + 1] = { 0 };
if (checkValue == true) {
tloge("Param err!\n");
return -1;
}
if (fp == NULL) {
if (realpath(filePath, realPath) != NULL && TEEC_CheckRealPathSuffix(realPath)) {
fpCur = fopen(realPath, "r");
}
if (fpCur == NULL) {
tloge("realpath open file error%d, path=%s\n", errno, filePath);
return -1;
}
fpUsable = fpCur;
} else {
fpUsable = fp;
}
ret = LoadSecFile(tzFd, fpUsable, LOAD_LIB, NULL, NULL);
if (fpCur != NULL) {
fclose(fpCur);
}
return ret;
}