/* * Copyright (C) 2022 Huawei Technologies Co., Ltd. * 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_api.h" #include <errno.h> /* for errno */ #include <fcntl.h> #include <limits.h> #include <pthread.h> #include <securec.h> #include <stdint.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/ioctl.h> /* for ioctl */ #include <sys/mman.h> /* for mmap */ #include <sys/stat.h> #include <sys/types.h> /* for open close */ #include "tc_ns_client.h" #include "tee_client_app_load.h" #include "tee_client_inner_api.h" #include "tee_client_list.h" #include "tee_get_native_cert.h" #include "tee_log.h" #include "tee_client_socket.h" #include "tee_auth_system.h" #define TEE_ERROR_CA_AUTH_FAIL 0xFFFFCFE5 #define AGENT_BUFF_SIZE 0x1000 #define CA_AUTH_RETRY_TIMES 30 #define H_OFFSET 32 #ifdef LOG_TAG #undef LOG_TAG #endif #define LOG_TAG "libteec_vendor" #define SHIFT 3 #define MASK 0x7 #define BYTE_BIT 8 void SetBit(uint32_t i, uint32_t byteMax, uint8_t *bitMap) { if ((i >> SHIFT) >= byteMax) { return; } if (bitMap == NULL) { return; } bitMap[i >> SHIFT] |= (1U << (i & MASK)); } void ClearBit(uint32_t i, uint32_t byteMax, uint8_t *bitMap) { if ((i >> SHIFT) >= byteMax) { return; } if (bitMap == NULL) { return; } bitMap[i >> SHIFT] &= ~(1U << (i & MASK)); } static void ClearBitWithLock(pthread_mutex_t *mutex, uint32_t i, uint32_t byteMax, uint8_t *bitMap) { if (pthread_mutex_lock(mutex) != 0) { tloge("get share mem bit lock failed\n"); return; } ClearBit(i, byteMax, bitMap); (void)pthread_mutex_unlock(mutex); } enum BitmapOps { SET, CLEAR }; static int32_t IterateBitmap(uint8_t *bitMap, uint32_t byteMax, enum BitmapOps ops) { uint32_t i, j; int32_t validBit = -1; uint8_t refByte = ((ops == SET) ? 0xff : 0); bool refBit = ((ops == SET) ? true : false); for (i = 0; i < byteMax; i++) { if (bitMap[i] == refByte) { continue; } for (j = 0; j < BYTE_BIT; j++) { bool bitZero = (bitMap[i] & (0x1 << j)) == 0; if (bitZero == refBit) { validBit = (int32_t)i * BYTE_BIT + (int32_t)j; break; } } if (validBit != -1) { break; } } if (validBit == -1) { return validBit; } if (ops == SET) { SetBit(validBit, byteMax, bitMap); } else { ClearBit(validBit, byteMax, bitMap); } return validBit; } int32_t GetAndSetBit(uint8_t *bitMap, uint32_t byteMax) { if (bitMap == NULL) { return -1; } return IterateBitmap(bitMap, byteMax, SET); } static int32_t GetAndSetBitWithLock(pthread_mutex_t *mutex, uint8_t *bitMap, uint32_t byteMax) { if (pthread_mutex_lock(mutex) != 0) { tloge("get share mem bit lock failed\n"); return -1; } int32_t validBit = GetAndSetBit(bitMap, byteMax); (void)pthread_mutex_unlock(mutex); return validBit; } int32_t GetAndCleartBit(uint8_t *bitMap, uint32_t byteMax) { if (bitMap == NULL) { return -1; } return IterateBitmap(bitMap, byteMax, CLEAR); } static pthread_mutex_t g_mutexAtom = PTHREAD_MUTEX_INITIALIZER; static void AtomInc(volatile uint32_t *cnt) { /* * The use of g_mutexAtom has been rigorously checked * and there is no risk of failure, we do not care the return value * of pthread_mutex_lock here */ (void)pthread_mutex_lock(&g_mutexAtom); (*cnt)++; (void)pthread_mutex_unlock(&g_mutexAtom); } static bool AtomDecAndCompareWithZero(volatile uint32_t *cnt) { bool result = false; /* * The use of g_mutexAtom has been rigorously checked * and there is no risk of failure, we do not care the return value * of pthread_mutex_lock here */ (void)pthread_mutex_lock(&g_mutexAtom); (*cnt)--; if ((*cnt) == 0) { result = true; } (void)pthread_mutex_unlock(&g_mutexAtom); return result; } static LIST_DECLARE(g_teecContextList); static pthread_mutex_t g_mutexTeecContext = PTHREAD_MUTEX_INITIALIZER; TEEC_Result TEEC_CheckOperation(TEEC_ContextInner *context, const TEEC_Operation *operation); static int32_t MutexLockContext(void) { int lockRet = pthread_mutex_lock(&g_mutexTeecContext); return lockRet; } static void MutexUnlockContext(int lockRet) { if (lockRet) { tloge("unlock mutex: not exe, mutex not in lock state. lockRet = %d\n", lockRet); return; } (void)pthread_mutex_unlock(&g_mutexTeecContext); } static TEEC_Result AddSessionList(uint32_t sessionId, const TEEC_UUID *destination, TEEC_ContextInner *context, TEEC_Session *session) { struct ListNode *node = NULL; struct ListNode *n = NULL; TEEC_Session *sessionEntry = NULL; session->session_id = sessionId; session->service_id = *destination; session->ops_cnt = 1; /* only for libteec, not for vendor ca */ int lockRet = pthread_mutex_lock(&context->sessionLock); if (lockRet != 0) { tloge("get session lock failed.\n"); return TEEC_ERROR_GENERIC; } /* if session is still in list, remove it */ LIST_FOR_EACH_SAFE(node, n, &context->session_list) { sessionEntry = CONTAINER_OF(node, TEEC_Session, head); if (sessionEntry == session) { ListRemoveEntry(node); } } ListInit(&session->head); ListInsertTail(&context->session_list, &session->head); AtomInc(&session->ops_cnt); /* only for libteec, not for vendor ca */ (void)pthread_mutex_unlock(&context->sessionLock); return TEEC_SUCCESS; } static TEEC_ContextInner *FindBnContext(const TEEC_Context *context) { TEEC_ContextInner *sContext = NULL; if (context == NULL) { tloge("find context: context is NULL!\n"); return NULL; } struct ListNode *ptr = NULL; if (!LIST_EMPTY(&g_teecContextList)) { LIST_FOR_EACH(ptr, &g_teecContextList) { TEEC_ContextInner *tmp = CONTAINER_OF(ptr, TEEC_ContextInner, c_node); if (tmp->fd == context->fd) { sContext = tmp; break; } } } return sContext; } TEEC_ContextInner *GetBnContext(const TEEC_Context *context) { TEEC_ContextInner *sContext = NULL; int retMutexLock = MutexLockContext(); if (retMutexLock != 0) { tloge("get context lock failed.\n"); return NULL; } sContext = FindBnContext(context); if (sContext != NULL) { AtomInc(&sContext->ops_cnt); } MutexUnlockContext(retMutexLock); return sContext; } bool PutBnContext(TEEC_ContextInner *context) { if (context == NULL) { tloge("put context: context is NULL!\n"); return false; } if (AtomDecAndCompareWithZero(&context->ops_cnt)) { TEEC_FinalizeContextInner(context); return true; } return false; } TEEC_ContextInner *FindAndRemoveBnContext(const TEEC_Context *context) { TEEC_ContextInner *sContext = NULL; int retMutexLock = MutexLockContext(); if (retMutexLock != 0) { tloge("get context lock failed.\n"); return NULL; } sContext = FindBnContext(context); if (sContext != NULL) { ListRemoveEntry(&sContext->c_node); } MutexUnlockContext(retMutexLock); return sContext; } static TEEC_Session *FindBnSession(const TEEC_Session *session, const TEEC_ContextInner *context) { TEEC_Session *sSession = NULL; struct ListNode *ptr = NULL; if (!LIST_EMPTY(&context->session_list)) { LIST_FOR_EACH(ptr, &context->session_list) { TEEC_Session *tmp = CONTAINER_OF(ptr, TEEC_Session, head); if (tmp->session_id == session->session_id) { sSession = tmp; break; } } } return sSession; } /* only for libteec, not for vendor ca */ TEEC_Session *GetBnSession(const TEEC_Session *session, TEEC_ContextInner *context) { TEEC_Session *sSession = NULL; if (session == NULL || context == NULL) { tloge("get session: context or session is NULL!\n"); return NULL; } int lockRet = pthread_mutex_lock(&context->sessionLock); if (lockRet != 0) { tloge("get session lock failed.\n"); return NULL; } sSession = FindBnSession(session, context); if (sSession != NULL) { AtomInc(&sSession->ops_cnt); } (void)pthread_mutex_unlock(&context->sessionLock); return sSession; } /* only for libteec, not for vendor ca */ void PutBnSession(TEEC_Session *session) { if (session == NULL) { tloge("put session: session is NULL!\n"); return; } if (AtomDecAndCompareWithZero(&session->ops_cnt)) { free(session); } return; } TEEC_Session *FindAndRemoveSession(const TEEC_Session *session, TEEC_ContextInner *context) { TEEC_Session *sSession = NULL; if (session == NULL || context == NULL) { tloge("find and remove session: context or session is NULL!\n"); return NULL; } int lockRet = pthread_mutex_lock(&context->sessionLock); if (lockRet != 0) { tloge("get session lock failed.\n"); return NULL; } sSession = FindBnSession(session, context); if (sSession != NULL) { ListRemoveEntry(&sSession->head); } (void)pthread_mutex_unlock(&context->sessionLock); return sSession; } static void ReleaseSharedMemory(TEEC_SharedMemoryInner *sharedMem) { bool condition = (sharedMem->is_allocated) && (sharedMem->buffer != NULL) && (sharedMem->buffer != ZERO_SIZE_PTR) && (sharedMem->size != 0); if (condition) { int32_t ret = ioctl(sharedMem->context->fd, (int)TC_NS_CLIENT_IOCTL_UNMAP_SHARED_MEM, sharedMem->buffer); if (ret) { tlogd("Release SharedMemory ioctl failed, maybe linux not support\n"); } ret = munmap(sharedMem->buffer, sharedMem->size); if (ret) { tloge("Release SharedMemory failed, munmap error\n"); } ClearBitWithLock(&sharedMem->context->shrMemBitMapLock, sharedMem->offset, sizeof(sharedMem->context->shm_bitmap), sharedMem->context->shm_bitmap); } sharedMem->buffer = NULL; sharedMem->size = 0; sharedMem->flags = 0; sharedMem->ops_cnt = 0; sharedMem->context = NULL; free(sharedMem); } void PutBnShrMem(TEEC_SharedMemoryInner *shrMem) { if (shrMem == NULL) { return; } if (AtomDecAndCompareWithZero(&shrMem->ops_cnt)) { ReleaseSharedMemory(shrMem); } return; } TEEC_SharedMemoryInner *GetBnShmByOffset(uint32_t shmOffset, TEEC_ContextInner *context) { TEEC_SharedMemoryInner *tempShardMem = NULL; if (context == NULL) { tloge("get shrmem offset: context is NULL!\n"); return NULL; } int lockRet = pthread_mutex_lock(&context->shrMemLock); if (lockRet != 0) { tloge("get shrmem lock failed.\n"); return NULL; } /* found server shardmem */ struct ListNode *ptr = NULL; if (!LIST_EMPTY(&context->shrd_mem_list)) { LIST_FOR_EACH(ptr, &context->shrd_mem_list) { tempShardMem = CONTAINER_OF(ptr, TEEC_SharedMemoryInner, head); if (tempShardMem->offset == shmOffset) { AtomInc(&tempShardMem->ops_cnt); (void)pthread_mutex_unlock(&context->shrMemLock); return tempShardMem; } } } (void)pthread_mutex_unlock(&context->shrMemLock); return NULL; } static TEEC_Result MallocShrMemInner(TEEC_SharedMemoryInner **shareMemInner) { errno_t nRet; TEEC_SharedMemoryInner *shmInner = (TEEC_SharedMemoryInner *)malloc(sizeof(*shmInner)); if (shmInner == NULL) { tloge("malloc shrmem: shmInner malloc failed\n"); return TEEC_FAIL; } nRet = memset_s(shmInner, sizeof(*shmInner), 0x00, sizeof(*shmInner)); if (nRet != EOK) { tloge("malloc shrmem: shmInner memset failed : %d\n", (int)nRet); free(shmInner); return TEEC_FAIL; } *shareMemInner = shmInner; return TEEC_SUCCESS; } static TEEC_Result TranslateRetValue(int32_t ret) { TEEC_Result teeRet; switch (ret) { case -EFAULT: teeRet = TEEC_ERROR_ACCESS_DENIED; break; case -ENOMEM: teeRet = TEEC_ERROR_OUT_OF_MEMORY; break; case -EINVAL: teeRet = TEEC_ERROR_BAD_PARAMETERS; break; default: teeRet = TEEC_ERROR_GENERIC; break; } return teeRet; } static uint32_t TranslateParamType(uint32_t flag) { uint32_t paramType; switch (flag) { case TEEC_MEM_INPUT: paramType = TEEC_MEMREF_PARTIAL_INPUT; break; case TEEC_MEM_OUTPUT: paramType = TEEC_MEMREF_PARTIAL_OUTPUT; break; case TEEC_MEM_INOUT: paramType = TEEC_MEMREF_PARTIAL_INOUT; break; default: paramType = TEEC_MEMREF_PARTIAL_INOUT; break; } return paramType; } static void TEEC_EncodeTempParam(const TEEC_TempMemoryReference *tempRef, TC_NS_ClientParam *param) { param->memref.buffer = (unsigned int)(uintptr_t)tempRef->buffer; param->memref.buffer_h_addr = ((unsigned long long)(uintptr_t)tempRef->buffer) >> H_OFFSET; param->memref.size_addr = (unsigned int)(uintptr_t)&tempRef->size; param->memref.size_h_addr = ((unsigned long long)(uintptr_t)&tempRef->size) >> H_OFFSET; } static void TEEC_EncodePartialParam(uint32_t paramType, const TEEC_RegisteredMemoryReference *memRef, TC_NS_ClientParam *param) { /* buffer offset len */ if (paramType == TEEC_MEMREF_WHOLE) { param->memref.offset = 0; param->memref.size_addr = (unsigned int)(uintptr_t)&memRef->parent->size; param->memref.size_h_addr = ((unsigned long long)(uintptr_t)&memRef->parent->size) >> H_OFFSET; } else { param->memref.offset = memRef->offset; param->memref.size_addr = (unsigned int)(uintptr_t)&memRef->size; param->memref.size_h_addr = ((unsigned long long)(uintptr_t)&memRef->size) >> H_OFFSET; } if (memRef->parent->is_allocated) { param->memref.buffer = (unsigned int)(uintptr_t)memRef->parent->buffer; param->memref.buffer_h_addr = ((unsigned long long)(uintptr_t)memRef->parent->buffer) >> H_OFFSET; } else { param->memref.buffer = (unsigned int)(uintptr_t)((unsigned char *)memRef->parent->buffer + memRef->offset); param->memref.buffer_h_addr = (unsigned long long)(uintptr_t)((unsigned char *)memRef->parent->buffer + memRef->offset) >> H_OFFSET; param->memref.offset = 0; } } static void TEEC_EncodeValueParam(const TEEC_Value *val, TC_NS_ClientParam *param) { param->value.a_addr = (unsigned int)(uintptr_t)&val->a; param->value.a_h_addr = ((unsigned long long)(uintptr_t)&val->a) >> H_OFFSET; param->value.b_addr = (unsigned int)(uintptr_t)&val->b; param->value.b_h_addr = ((unsigned long long)(uintptr_t)&val->b) >> H_OFFSET; } static void TEEC_EncodeIonParam(const TEEC_IonReference *ionRef, TC_NS_ClientParam *param) { param->value.a_addr = (unsigned int)(uintptr_t)&ionRef->ionShareFd; param->value.a_h_addr = (unsigned int)(((unsigned long long)(uintptr_t)&ionRef->ionShareFd) >> H_OFFSET); param->value.b_addr = (unsigned int)(uintptr_t)&ionRef->ionSize; param->value.b_h_addr = (unsigned int)(((unsigned long long)(uintptr_t)&ionRef->ionSize) >> H_OFFSET); } static void TEEC_EncodeParam(TC_NS_ClientContext *cliContext, const TEEC_Operation *operation) { uint32_t paramType[TEEC_PARAM_NUM]; uint32_t paramCnt; uint32_t diff; diff = (uint32_t)TEEC_MEMREF_PARTIAL_INPUT - (uint32_t)TEEC_MEMREF_TEMP_INPUT; for (paramCnt = 0; paramCnt < TEEC_PARAM_NUM; paramCnt++) { paramType[paramCnt] = TEEC_PARAM_TYPE_GET(operation->paramTypes, paramCnt); bool checkValue = (paramType[paramCnt] == TEEC_ION_INPUT || paramType[paramCnt] == TEEC_ION_SGLIST_INPUT); if (IS_TEMP_MEM(paramType[paramCnt])) { TEEC_EncodeTempParam(&operation->params[paramCnt].tmpref, &cliContext->params[paramCnt]); } else if (IS_PARTIAL_MEM(paramType[paramCnt])) { const TEEC_RegisteredMemoryReference *memref = &operation->params[paramCnt].memref; TEEC_EncodePartialParam(paramType[paramCnt], memref, &cliContext->params[paramCnt]); /* translate the paramType to know the driver */ if (paramType[paramCnt] == TEEC_MEMREF_WHOLE) { paramType[paramCnt] = TranslateParamType(memref->parent->flags); } /* if is not allocated, * translate TEEC_MEMREF_PARTIAL_XXX to TEEC_MEMREF_TEMP_XXX */ if (!memref->parent->is_allocated) { paramType[paramCnt] = paramType[paramCnt] - diff; } } else if (IS_VALUE_MEM(paramType[paramCnt])) { TEEC_EncodeValueParam(&operation->params[paramCnt].value, &cliContext->params[paramCnt]); } else if (checkValue == true) { TEEC_EncodeIonParam(&operation->params[paramCnt].ionref, &cliContext->params[paramCnt]); } } cliContext->paramTypes = TEEC_PARAM_TYPES(paramType[0], paramType[1], paramType[2], paramType[3]); tlogd("cli param type %x\n", cliContext->paramTypes); return; } static TEEC_Result TEEC_Encode(TC_NS_ClientContext *cliContext, const TEEC_Session *session, uint32_t cmdId, const TC_NS_ClientLogin *cliLogin, const TEEC_Operation *operation) { errno_t rc; rc = memset_s(cliContext, sizeof(TC_NS_ClientContext), 0x00, sizeof(TC_NS_ClientContext)); if (rc != EOK) { return (TEEC_Result)TEEC_ERROR_BAD_PARAMETERS; } cliContext->session_id = session->session_id; cliContext->cmd_id = cmdId; cliContext->returns.code = 0; cliContext->returns.origin = TEEC_ORIGIN_API; cliContext->login.method = cliLogin->method; cliContext->login.mdata = cliLogin->mdata; rc = memcpy_s(cliContext->uuid, sizeof(cliContext->uuid), (uint8_t *)(&session->service_id), sizeof(TEEC_UUID)); if (rc != EOK) { return (TEEC_Result)TEEC_ERROR_BAD_PARAMETERS; } if ((operation == NULL) || (!operation->paramTypes)) { return TEEC_SUCCESS; } cliContext->started = operation->cancel_flag; TEEC_EncodeParam(cliContext, operation); return TEEC_SUCCESS; } #ifdef LIB_TEEC_VENDOR static int CaDaemonConnectWithoutCaInfo(void) { int ret; errno_t rc; CaAuthInfo caInfo; rc = memset_s(&caInfo, sizeof(caInfo), 0, sizeof(caInfo)); if (rc != EOK) { return -1; } ret = CaDaemonConnectWithCaInfo(&caInfo, GET_FD); return ret; } #else static int32_t ObtainTzdriveFd(void) { int32_t fd = open(TC_NS_CLIENT_DEV_NAME, O_RDWR); if (fd < 0) { tloge("open tzdriver fd failed\n"); return -1; } return fd; } static int32_t SetLoginInfo(const CaAuthInfo *caInfo, int32_t fd) { int32_t ret; int32_t rc = 0; uint32_t bufLen = BUF_MAX_SIZE; uint8_t *buf = (uint8_t *)malloc(bufLen); if (buf == NULL) { tloge("malloc failed\n"); return -1; } ret = memset_s(buf, bufLen, 0, bufLen); if (ret != EOK) { tloge("memset buf failed\n"); goto END; } switch (caInfo->type) { case SYSTEM_CA: tlogd("system ca type\n"); rc = TeeGetNativeCert(caInfo->pid, caInfo->uid, &bufLen, buf); break; case SA_CA: tlogd("sa ca type\n"); rc = TEEGetNativeSACaInfo(caInfo, buf, bufLen); break; case APP_CA: tlogd("hap ca type\n"); if (memcpy_s(buf, bufLen, caInfo->certs, sizeof(caInfo->certs)) != EOK) { tloge("memcpy hap cainfo failed\n"); rc = -1; } break; default: tloge("invaild ca type %d\n", caInfo->type); rc = -1; break; } if (rc != 0) { /* Inform the driver the cert could not be set */ ret = ioctl(fd, TC_NS_CLIENT_IOCTL_LOGIN, NULL); } else { ret = ioctl(fd, TC_NS_CLIENT_IOCTL_LOGIN, buf); } if (ret != 0) { tloge("Failed to set login information for client err = %d, %d\n", ret, caInfo->type); } else { ret = rc; } END: free(buf); buf = NULL; return ret; } #endif TEEC_Result TEEC_InitializeContextInner(TEEC_ContextInner *context, const CaAuthInfo *caInfo) { if (context == NULL) { tloge("Initial context: context is NULL\n"); return TEEC_ERROR_BAD_PARAMETERS; } #ifdef LIB_TEEC_VENDOR int32_t fd = CaDaemonConnectWithoutCaInfo(); if (fd < 0) { tloge("connect() failed, fd %d", fd); return TEEC_ERROR_GENERIC; } #else if (caInfo == NULL) { return TEEC_ERROR_BAD_PARAMETERS; } int32_t fd = ObtainTzdriveFd(); if (fd < 0) { tloge("obtain fd failed\n"); return TEEC_ERROR_BAD_PARAMETERS; } if (SetLoginInfo(caInfo, fd) != TEEC_SUCCESS) { tloge("set login failed\n"); close(fd); return TEEC_ERROR_GENERIC; } #endif context->fd = (uint32_t)fd; context->ops_cnt = 1; ListInit(&context->session_list); ListInit(&context->shrd_mem_list); errno_t nRet = memset_s(context->shm_bitmap, sizeof(context->shm_bitmap), 0x00, sizeof(context->shm_bitmap)); if (nRet != EOK) { tloge("Initial context: context->shm_bitmap memset failed : %d\n", (int)nRet); close((int)context->fd); return TEEC_FAIL; } int retMutexLock = MutexLockContext(); if (retMutexLock != 0) { tloge("get context lock failed.\n"); close((int)context->fd); return TEEC_FAIL; } (void)pthread_mutex_init(&context->sessionLock, NULL); (void)pthread_mutex_init(&context->shrMemLock, NULL); (void)pthread_mutex_init(&context->shrMemBitMapLock, NULL); ListInsertTail(&g_teecContextList, &context->c_node); AtomInc(&context->ops_cnt); MutexUnlockContext(retMutexLock); return TEEC_SUCCESS; } /* * Function: TEEC_InitializeContext * Description: This function initializes a new TEE Context, forming a connection between * this Client Application and the TEE identified by the string identifier name. * Parameters: name: a zero-terminated string that describes the TEE to connect to. * If this parameter is set to NULL, the Implementation MUST select a default TEE. * context: a TEEC_Context structure that be initialized by the Implementation. * Return: TEEC_SUCCESS: the initialization was successful. * other: initialization was not successful. */ TEEC_Result TEEC_InitializeContext(const char *name, TEEC_Context *context) { TEEC_Result ret; errno_t nRet; uint32_t conCxt = 0; struct ListNode *ptr = NULL; (void)name; if (context == NULL) { tloge("initialize context: context is NULL\n"); return TEEC_ERROR_BAD_PARAMETERS; } if (!LIST_EMPTY(&g_teecContextList)) { LIST_FOR_EACH(ptr, &g_teecContextList) { conCxt++; } } if (conCxt >= MAX_CXTCNT_ONECA) { tloge("the contexts is already full, please finalize some of them\n"); return TEEC_FAIL; } TEEC_ContextInner *contextInner = (TEEC_ContextInner *)malloc(sizeof(*contextInner)); if (contextInner == NULL) { tloge("initialize context: Failed to malloc teec contextInner\n"); return TEEC_ERROR_GENERIC; } nRet = memset_s(contextInner, sizeof(*contextInner), 0, sizeof(*contextInner)); if (nRet != EOK) { tloge("initialize context: contextInner memset failed : %d\n", (int)nRet); free(contextInner); return TEEC_FAIL; } ret = TEEC_InitializeContextInner(contextInner, NULL); if (ret == TEEC_SUCCESS) { context->fd = contextInner->fd; context->ta_path = NULL; ListInit(&context->session_list); ListInit(&context->shrd_mem_list); (void)PutBnContext(contextInner); /* pair with ops_cnt++ when add to list */ return TEEC_SUCCESS; } tloge("initialize context: failed:0x%x\n", ret); free(contextInner); return ret; } /* * Function: TEEC_FinalizeContext * Description: This function finalizes an initialized TEE Context. * Parameters: context: an initialized TEEC_Context structure which is to be finalized. * Return: NULL */ void TEEC_FinalizeContextInner(TEEC_ContextInner *context) { struct ListNode *ptr = NULL; struct ListNode *n = NULL; TEEC_Session *session = NULL; TEEC_SharedMemoryInner *shrdMem = NULL; /* First, check parameters is valid or not */ if (context == NULL) { tloge("finalize context: context is NULL\n"); return; } int lockRet = pthread_mutex_lock(&context->sessionLock); if (lockRet != 0) { tloge("get session lock failed.\n"); return; } if (!LIST_EMPTY(&context->session_list)) { tlogd("context still has sessions opened, close it\n"); LIST_FOR_EACH_SAFE(ptr, n, &context->session_list) { session = CONTAINER_OF(ptr, TEEC_Session, head); ListRemoveEntry(&session->head); TEEC_CloseSessionInner(session, context); /* for service */ if (context->callFromService) { PutBnSession(session); /* pair with open session */ session = NULL; } } } (void)pthread_mutex_unlock(&context->sessionLock); lockRet = pthread_mutex_lock(&context->shrMemLock); if (lockRet != 0) { tloge("get shrmem lock failed.\n"); return; } if (!LIST_EMPTY(&context->shrd_mem_list)) { tlogd("context contains unreleased Shared Memory blocks, release it\n"); LIST_FOR_EACH_SAFE(ptr, n, &context->shrd_mem_list) { shrdMem = CONTAINER_OF(ptr, TEEC_SharedMemoryInner, head); ListRemoveEntry(&shrdMem->head); PutBnShrMem(shrdMem); /* pair with Initial value 1 */ } } (void)pthread_mutex_unlock(&context->shrMemLock); close((int)context->fd); context->fd = -1; (void)pthread_mutex_destroy(&context->sessionLock); (void)pthread_mutex_destroy(&context->shrMemLock); free(context); } void TEEC_FinalizeContext(TEEC_Context *context) { if (context == NULL) { tloge("finalize context: context is NULL\n"); return; } TEEC_ContextInner *contextInner = FindAndRemoveBnContext(context); (void)PutBnContext(contextInner); /* pair with initialize context */ context->fd = -1; } static TEEC_Result TEEC_DoOpenSession(int fd, TC_NS_ClientContext *cliContext, const TEEC_UUID *destination, TEEC_ContextInner *context, TEEC_Session *session) { int32_t ret; TEEC_Result teecRet; int i = CA_AUTH_RETRY_TIMES; do { cliContext->returns.code = 0; cliContext->returns.origin = TEEC_ORIGIN_API; ret = ioctl(fd, (int)TC_NS_CLIENT_IOCTL_SES_OPEN_REQ, cliContext); } while (((TEEC_Result)cliContext->returns.code == TEE_ERROR_CA_AUTH_FAIL) && i--); if (ret < 0) { tloge("open session failed, ioctl errno = %d\n", ret); teecRet = TranslateRetValue(ret); cliContext->returns.origin = TEEC_ORIGIN_COMMS; return teecRet; } else if (ret > 0) { tloge("open session failed(%d), code=0x%x, origin=%u\n", ret, cliContext->returns.code, cliContext->returns.origin); if (cliContext->returns.code) { teecRet = (TEEC_Result)cliContext->returns.code; } else { teecRet = (TEEC_Result)TEEC_ERROR_GENERIC; } return teecRet; } return AddSessionList(cliContext->session_id, destination, context, session); } TEEC_Result TEEC_OpenSessionInner(int callingPid, const TaFileInfo *taFile, TEEC_ContextInner *context, TEEC_Session *session, const TEEC_UUID *destination, uint32_t connectionMethod, const void *connectionData, TEEC_Operation *operation, uint32_t *returnOrigin) { TEEC_Result teecRet = (TEEC_Result)TEEC_ERROR_BAD_PARAMETERS; TC_NS_ClientContext cliContext; TC_NS_ClientLogin cliLogin = { 0, 0 }; /* prefirst, we set origin be zero */ cliContext.returns.origin = TEEC_ORIGIN_API; cliContext.file_buffer = NULL; bool condition = (context == NULL) || (taFile == NULL) || (session == NULL) || (destination == NULL); if (condition) { tloge("context or session or destination is NULL\n"); goto ERROR; } /* now only support TEEC_LOGIN_IDENTIFY */ condition = (connectionMethod != TEEC_LOGIN_IDENTIFY) || (connectionData != NULL); if (condition) { tloge("Login method is not supported or connection data is not null\n"); goto ERROR; } cliLogin.method = connectionMethod; if (operation != NULL) { /* Params 2 and 3 are used for ident by teecd hence ->TEEC_NONE */ operation->paramTypes = TEEC_PARAM_TYPES(TEEC_PARAM_TYPE_GET(operation->paramTypes, 0), TEEC_PARAM_TYPE_GET(operation->paramTypes, 1), TEEC_NONE, TEEC_NONE); } teecRet = TEEC_CheckOperation(context, operation); if (teecRet != TEEC_SUCCESS) { tloge("operation is invalid\n"); goto ERROR; } /* Paramters right, start execution */ /* * note:before open session success, we should send session=0 as initial state. */ teecRet = TEEC_Encode(&cliContext, session, GLOBAL_CMD_ID_OPEN_SESSION, &cliLogin, operation); if (teecRet != TEEC_SUCCESS) { tloge("OpenSession: teec encode failed(0x%x)!\n", teecRet); goto ERROR; } cliContext.callingPid = (unsigned int)callingPid; int32_t ret = TEEC_GetApp(taFile, destination, &cliContext); if (ret < 0) { tloge("get app error\n"); teecRet = (TEEC_Result)TEEC_ERROR_TRUSTED_APP_LOAD_ERROR; goto ERROR; } teecRet = TEEC_DoOpenSession(context->fd, &cliContext, destination, context, session); ERROR: /* ONLY when ioctl returnCode!=0 and returnOrigin not NULL, * set *returnOrigin */ if (returnOrigin != NULL) { *returnOrigin = cliContext.returns.origin; } if (cliContext.file_buffer != NULL) { free(cliContext.file_buffer); } return teecRet; } /* * Function: TEEC_OpenSession * Description: This function opens a new Session * Parameters: context: a pointer to an initialized TEE Context. * session: a pointer to a Session structure to open. * destination: a pointer to a UUID structure. * connectionMethod: the method of connection to use. * connectionData: any necessary data required to support the connection method chosen. * operation: a pointer to an Operation containing a set of Parameters. * returnOrigin: a pointer to a variable which will contain the return origin. * Return: TEEC_SUCCESS: success * other: failure */ TEEC_Result TEEC_OpenSession(TEEC_Context *context, TEEC_Session *session, const TEEC_UUID *destination, uint32_t connectionMethod, const void *connectionData, TEEC_Operation *operation, uint32_t *returnOrigin) { TEEC_Result ret = TEEC_ERROR_BAD_PARAMETERS; uint32_t retOrigin = TEEC_ORIGIN_API; if ((context == NULL) || (session == NULL) || (destination == NULL)) { tloge("param is null!\n"); goto END; } /* * ca may call closesession even if opensession failed, * we set session->context here to avoid receive a illegal ptr */ session->context = context; session->service_id = *destination; session->session_id = 0; TaFileInfo taFile; taFile.taFp = NULL; taFile.taPath = context->ta_path; TEEC_ContextInner *contextInner = GetBnContext(context); if (contextInner == NULL) { tloge("no context found!\n"); goto END; } ret = TEEC_OpenSessionInner(0, &taFile, contextInner, session, destination, connectionMethod, connectionData, operation, &retOrigin); (void)PutBnContext(contextInner); END: if (returnOrigin != NULL) { *returnOrigin = retOrigin; } return ret; } void TEEC_CloseSessionInner(TEEC_Session *session, const TEEC_ContextInner *context) { int32_t ret; TC_NS_ClientContext cliContext; TC_NS_ClientLogin cliLogin = { 0, 0 }; TEEC_Result teecRet; /* First, check parameters is valid or not */ if ((session == NULL) || (context == NULL)) { tloge("close session: session or context is NULL\n"); return; } teecRet = TEEC_Encode(&cliContext, session, GLOBAL_CMD_ID_CLOSE_SESSION, &cliLogin, NULL); if (teecRet != TEEC_SUCCESS) { tloge("close session: teec encode failed(0x%x)!\n", teecRet); return; } ret = ioctl((int)context->fd, (int)TC_NS_CLIENT_IOCTL_SES_CLOSE_REQ, &cliContext); if (ret != 0) { tloge("close session failed, ret=0x%x\n", ret); } session->session_id = 0; session->context = NULL; errno_t rc = memset_s((uint8_t *)&session->service_id, sizeof(session->service_id), 0x00, sizeof(session->service_id)); if (rc != EOK) { tloge("memset service id fail\n"); } return; } /* * Function: TEEC_CloseSession * Description: This function closes an opened Session. * Parameters: session: the session to close. * Return: NULL */ void TEEC_CloseSession(TEEC_Session *session) { if ((session == NULL) || (session->context == NULL)) { tloge("close session: session or session->context is NULL\n"); return; } TEEC_ContextInner *contextInner = GetBnContext(session->context); if (contextInner == NULL) { tloge("context is NULL\n"); return; } TEEC_Session *sessionInList = FindAndRemoveSession(session, contextInner); if (sessionInList == NULL) { tloge("session is not in the context list\n"); (void)PutBnContext(contextInner); return; } TEEC_CloseSessionInner(session, contextInner); (void)PutBnContext(contextInner); } static TEEC_Result ProcessInvokeCommand(const TEEC_ContextInner *context, TC_NS_ClientContext *cliContext) { TEEC_Result teecRet; int32_t ret = ioctl((int)context->fd, (int)TC_NS_CLIENT_IOCTL_SEND_CMD_REQ, cliContext); if (ret == 0) { tlogd("invoke cmd success\n"); teecRet = TEEC_SUCCESS; } else if (ret < 0) { teecRet = TranslateRetValue(-errno); tloge("invoke cmd failed, ioctl errno = %d\n", errno); cliContext->returns.origin = TEEC_ORIGIN_COMMS; } else { tloge("invoke cmd failed(%d), code=0x%x, origin=%u\n", ret, cliContext->returns.code, cliContext->returns.origin); if (cliContext->returns.code) { teecRet = (TEEC_Result)cliContext->returns.code; } else { teecRet = (TEEC_Result)TEEC_ERROR_GENERIC; } } return teecRet; } TEEC_Result TEEC_InvokeCommandInner(TEEC_ContextInner *context, const TEEC_Session *session, uint32_t commandID, const TEEC_Operation *operation, uint32_t *returnOrigin) { TEEC_Result teecRet = (TEEC_Result)TEEC_ERROR_BAD_PARAMETERS; TC_NS_ClientContext cliContext; TC_NS_ClientLogin cliLogin = { 0, 0 }; /* prefirst, we set origin be zero */ cliContext.returns.origin = TEEC_ORIGIN_API; /* First, check parameters is valid or not */ if ((session == NULL) || (context == NULL)) { goto ERROR; } teecRet = TEEC_CheckOperation(context, operation); if (teecRet != TEEC_SUCCESS) { tloge("operation is invalid\n"); goto ERROR; } /* Paramters all right, start execution */ teecRet = TEEC_Encode(&cliContext, session, commandID, &cliLogin, operation); if (teecRet != TEEC_SUCCESS) { tloge("InvokeCommand: teec encode failed(0x%x)!\n", teecRet); goto ERROR; } teecRet = ProcessInvokeCommand(context, &cliContext); if (teecRet != TEEC_SUCCESS) { tloge("InvokeCommand failed\n"); } ERROR: /* ONLY when ioctl returnCode!=0 and returnOrigin not NULL, * set *returnOrigin */ if (returnOrigin != NULL) { *returnOrigin = cliContext.returns.origin; } return teecRet; } /* * Function: TEEC_InvokeCommand * Description: This function invokes a Command within the specified Session. * Parameters: session: the open Session in which the command will be invoked. * commandID: the identifier of the Command. * operation: a pointer to an Operation containing a set of Parameters. * returnOrigin: a pointer to a variable which will contain the return origin. * Return: TEEC_SUCCESS: success * other: failure */ TEEC_Result TEEC_InvokeCommand(TEEC_Session *session, uint32_t commandID, TEEC_Operation *operation, uint32_t *returnOrigin) { TEEC_Result ret = TEEC_ERROR_BAD_PARAMETERS; uint32_t retOrigin = TEEC_ORIGIN_API; if ((session == NULL) || (session->context == NULL)) { tloge("invoke failed, session or session context is null\n"); goto END; } TEEC_Context *contextTemp = session->context; TEEC_ContextInner *contextInner = GetBnContext(session->context); ret = TEEC_InvokeCommandInner(contextInner, session, commandID, operation, &retOrigin); if (ret == TEEC_SUCCESS) { session->context = contextTemp; } (void)PutBnContext(contextInner); END: if (returnOrigin != NULL) { *returnOrigin = retOrigin; } return ret; } TEEC_Result TEEC_RegisterSharedMemoryInner(TEEC_ContextInner *context, TEEC_SharedMemoryInner *sharedMem) { /* First, check parameters is valid or not */ if ((context == NULL) || (sharedMem == NULL)) { tloge("register shardmem: context or sharedMem is NULL\n"); return (TEEC_Result)TEEC_ERROR_BAD_PARAMETERS; } bool condition = (sharedMem->buffer == NULL) || ((sharedMem->flags != TEEC_MEM_INPUT) && (sharedMem->flags != TEEC_MEM_OUTPUT) && (sharedMem->flags != TEEC_MEM_INOUT)); if (condition) { tloge("register shardmem: sharedMem->flags wrong\n"); return (TEEC_Result)TEEC_ERROR_BAD_PARAMETERS; } /* Paramters all right, start execution */ sharedMem->ops_cnt = 1; sharedMem->is_allocated = false; sharedMem->offset = (uint32_t)(-1); sharedMem->context = context; ListInit(&sharedMem->head); int lockRet = pthread_mutex_lock(&context->shrMemLock); if (lockRet != 0) { tloge("get share mem lock failed.\n"); return TEEC_ERROR_GENERIC; } ListInsertTail(&context->shrd_mem_list, &sharedMem->head); AtomInc(&sharedMem->ops_cnt); (void)pthread_mutex_unlock(&context->shrMemLock); return TEEC_SUCCESS; } /* * Function: TEEC_RegisterSharedMemory * Description: This function registers a block of existing Client Application memory * as a block of Shared Memory within the scope of the specified TEE Context. * Parameters: context: a pointer to an initialized TEE Context. * sharedMem: a pointer to a Shared Memory structure to register. * Return: TEEC_SUCCESS: success * other: failure */ TEEC_Result TEEC_RegisterSharedMemory(TEEC_Context *context, TEEC_SharedMemory *sharedMem) { TEEC_Result ret; TEEC_ContextInner *contextInner = NULL; TEEC_SharedMemoryInner *shmInner = NULL; bool condition = (context == NULL) || (sharedMem == NULL); if (condition) { tloge("register shardmem: context or sharedMem is NULL\n"); return (TEEC_Result)TEEC_ERROR_BAD_PARAMETERS; } /* * ca may call ReleaseShareMemory even if RegisterShareMem failed, * we set sharedMem->context here to avoid receive a illegal ptr */ sharedMem->context = context; ret = MallocShrMemInner(&shmInner); if (ret != TEEC_SUCCESS) { return ret; } shmInner->buffer = sharedMem->buffer; shmInner->size = sharedMem->size; shmInner->flags = sharedMem->flags; contextInner = GetBnContext(context); ret = TEEC_RegisterSharedMemoryInner(contextInner, shmInner); if (ret == TEEC_SUCCESS) { sharedMem->ops_cnt = shmInner->ops_cnt; sharedMem->is_allocated = shmInner->is_allocated; ListInit(&sharedMem->head); PutBnShrMem(shmInner); /* pair with ops_cnt++ when add to list */ (void)PutBnContext(contextInner); return ret; } tloge("register shardmem: failed:0x%x\n", ret); (void)PutBnContext(contextInner); free(shmInner); return ret; } static void RelaseBufferAndClearBit(TEEC_ContextInner *context, TEEC_SharedMemoryInner *sharedMem) { if (sharedMem->buffer != MAP_FAILED && sharedMem->size != 0) { (void)munmap(sharedMem->buffer, sharedMem->size); } ClearBitWithLock(&context->shrMemBitMapLock, sharedMem->offset, sizeof(context->shm_bitmap), context->shm_bitmap); sharedMem->buffer = NULL; sharedMem->offset = 0; } TEEC_Result TEEC_AllocateSharedMemoryInner(TEEC_ContextInner *context, TEEC_SharedMemoryInner *sharedMem) { TEEC_Result ret; /* First, check parameters is valid or not */ if ((context == NULL) || (sharedMem == NULL)) { tloge("allocate shardmem: context or sharedMem is NULL\n"); return (TEEC_Result)TEEC_ERROR_BAD_PARAMETERS; } bool condition = (sharedMem->flags != TEEC_MEM_INPUT) && (sharedMem->flags != TEEC_MEM_OUTPUT) && (sharedMem->flags != TEEC_MEM_INOUT); if (condition) { tloge("allocate shardmem: sharedMem->flags wrong\n"); return (TEEC_Result)TEEC_ERROR_BAD_PARAMETERS; } /* Paramters all right, start execution */ sharedMem->buffer = NULL; int32_t validBit = GetAndSetBitWithLock(&context->shrMemBitMapLock, context->shm_bitmap, sizeof(context->shm_bitmap)); if (validBit < 0) { tloge("get valid bit for shm failed\n"); return (TEEC_Result)TEEC_ERROR_BAD_PARAMETERS; } sharedMem->offset = (uint32_t)validBit; if (sharedMem->size != 0) { sharedMem->buffer = mmap(0, (unsigned long)sharedMem->size, (PROT_READ | PROT_WRITE), MAP_SHARED, (int)context->fd, (long)(sharedMem->offset * PAGE_SIZE)); } else { sharedMem->buffer = ZERO_SIZE_PTR; } if (sharedMem->buffer == MAP_FAILED) { tloge("mmap failed\n"); ret = TEEC_ERROR_OUT_OF_MEMORY; goto ERROR; } sharedMem->ops_cnt = 1; sharedMem->is_allocated = true; sharedMem->context = context; ListInit(&sharedMem->head); if (pthread_mutex_lock(&context->shrMemLock) != 0) { tloge("get share mem lock failed\n"); ret = TEEC_ERROR_GENERIC; goto ERROR; } ListInsertTail(&context->shrd_mem_list, &sharedMem->head); AtomInc(&sharedMem->ops_cnt); (void)pthread_mutex_unlock(&context->shrMemLock); return TEEC_SUCCESS; ERROR: RelaseBufferAndClearBit(context, sharedMem); return ret; } /* * Function: TEEC_AllocateSharedMemory * Description: This function allocates a new block of memory as a block of * Shared Memory within the scope of the specified TEE Context. * Parameters: context: a pointer to an initialized TEE Context. * sharedMem: a pointer to a Shared Memory structure to allocate. * Return: TEEC_SUCCESS: success * other: failure */ TEEC_Result TEEC_AllocateSharedMemory(TEEC_Context *context, TEEC_SharedMemory *sharedMem) { TEEC_Result ret; TEEC_ContextInner *contextInner = NULL; TEEC_SharedMemoryInner *shmInner = NULL; bool condition = (context == NULL) || (sharedMem == NULL); if (condition) { tloge("allocate shardmem: context or sharedMem is NULL\n"); return (TEEC_Result)TEEC_ERROR_BAD_PARAMETERS; } /* * ca may call ReleaseShareMemory even if AllocateSharedMemory failed, * we set sharedMem->context here to avoid receive a illegal ptr */ sharedMem->context = context; ret = MallocShrMemInner(&shmInner); if (ret != TEEC_SUCCESS) { return ret; } shmInner->size = sharedMem->size; shmInner->flags = sharedMem->flags; contextInner = GetBnContext(context); ret = TEEC_AllocateSharedMemoryInner(contextInner, shmInner); if (ret == TEEC_SUCCESS) { sharedMem->buffer = shmInner->buffer; sharedMem->ops_cnt = shmInner->ops_cnt; sharedMem->is_allocated = shmInner->is_allocated; ListInit(&sharedMem->head); PutBnShrMem(shmInner); /* pair with ops_cnt++ when add to list */ (void)PutBnContext(contextInner); return TEEC_SUCCESS; } tloge("allocate shardmem: failed:0x%x\n", ret); (void)PutBnContext(contextInner); free(shmInner); return ret; } static bool TEEC_FindAndRemoveShrMemInner(TEEC_SharedMemoryInner **sharedMem, TEEC_ContextInner *contextInner) { bool found = false; struct ListNode *ptr = NULL; TEEC_SharedMemoryInner *tempSharedMem = NULL; TEEC_SharedMemoryInner *shm = *sharedMem; int lockRet = pthread_mutex_lock(&contextInner->shrMemLock); if (lockRet != 0) { tloge("get share mem lock failed.\n"); return false; } LIST_FOR_EACH(ptr, &contextInner->shrd_mem_list) { tempSharedMem = CONTAINER_OF(ptr, TEEC_SharedMemoryInner, head); if (tempSharedMem->offset == shm->offset && tempSharedMem->context == shm->context) { found = true; ListRemoveEntry(&tempSharedMem->head); *sharedMem = tempSharedMem; break; } } (void)pthread_mutex_unlock(&contextInner->shrMemLock); return found; } void TEEC_ReleaseSharedMemoryInner(TEEC_SharedMemoryInner *sharedMem) { TEEC_SharedMemoryInner *shm = sharedMem; /* First, check parameters is valid or not */ if ((shm == NULL) || (shm->context == NULL)) { tloge("Shared Memory is NULL\n"); return; } bool found = TEEC_FindAndRemoveShrMemInner(&shm, shm->context); if (!found) { tloge("Shared Memory is not in the list\n"); return; } tloge("Shared Memory found\n"); PutBnShrMem(shm); /* pair with Initial value 1 */ } /* * Function: TEEC_ReleaseSharedMemory * Description: This function deregisters or deallocates a previously initialized * block of Shared Memory.. * Parameters: sharedMem: a pointer to a valid Shared Memory structure. * Return: NULL */ void TEEC_ReleaseSharedMemory(TEEC_SharedMemory *sharedMem) { if ((sharedMem == NULL) || (sharedMem->context == NULL)) { tloge("release shardmem: sharedMem or sharedMem->context is NULL\n"); return; } TEEC_SharedMemoryInner shmInner = { 0 }; shmInner.buffer = sharedMem->buffer; shmInner.size = sharedMem->size; shmInner.flags = sharedMem->flags; shmInner.ops_cnt = sharedMem->ops_cnt; shmInner.is_allocated = sharedMem->is_allocated; TEEC_ContextInner *contextInner = GetBnContext(sharedMem->context); shmInner.context = contextInner; TEEC_ReleaseSharedMemoryInner(&shmInner); (void)PutBnContext(contextInner); sharedMem->buffer = NULL; sharedMem->size = 0; sharedMem->flags = 0; sharedMem->ops_cnt = 0; sharedMem->context = NULL; } static TEEC_Result TEEC_CheckTmpRef(TEEC_TempMemoryReference tmpref) { if ((tmpref.buffer == NULL) || (tmpref.size == 0)) { tloge("tmpref buffer is null, or size is zero\n"); return (TEEC_Result)TEEC_ERROR_BAD_PARAMETERS; } return (TEEC_Result)TEEC_SUCCESS; } static bool CheckSharedBufferExist(TEEC_ContextInner *context, const TEEC_RegisteredMemoryReference *sharedMem) { if (context == NULL) { return false; } struct ListNode *ptr = NULL; TEEC_SharedMemoryInner *tempSharedMem = NULL; TEEC_SharedMemory *shm = sharedMem->parent; int lockRet = pthread_mutex_lock(&context->shrMemLock); if (lockRet != 0) { tloge("get share mem lock failed\n"); return false; } LIST_FOR_EACH(ptr, &context->shrd_mem_list) { tempSharedMem = CONTAINER_OF(ptr, TEEC_SharedMemoryInner, head); if (tempSharedMem->buffer == shm->buffer) { (void)pthread_mutex_unlock(&context->shrMemLock); return true; } } (void)pthread_mutex_unlock(&context->shrMemLock); return false; } static TEEC_Result TEEC_CheckMemRef(TEEC_ContextInner *context, TEEC_RegisteredMemoryReference memref, uint32_t paramType) { bool condition = (memref.parent == NULL) || (memref.parent->buffer == NULL); if (condition) { tloge("parent of memref is null, or the buffer is zero\n"); return (TEEC_Result)TEEC_ERROR_BAD_PARAMETERS; } if (paramType == TEEC_MEMREF_PARTIAL_INPUT) { if (!(memref.parent->flags & TEEC_MEM_INPUT)) { goto PARAM_ERROR; } } else if (paramType == TEEC_MEMREF_PARTIAL_OUTPUT) { if (!(memref.parent->flags & TEEC_MEM_OUTPUT)) { goto PARAM_ERROR; } } else if (paramType == TEEC_MEMREF_PARTIAL_INOUT) { if (!(memref.parent->flags & TEEC_MEM_INPUT)) { goto PARAM_ERROR; } if (!(memref.parent->flags & TEEC_MEM_OUTPUT)) { goto PARAM_ERROR; } } else { /* if type is TEEC_MEMREF_WHOLE, ignore it */ } condition = (paramType == TEEC_MEMREF_PARTIAL_INPUT) || (paramType == TEEC_MEMREF_PARTIAL_OUTPUT) || (paramType == TEEC_MEMREF_PARTIAL_INOUT); if (condition) { if ((memref.offset + memref.size) > memref.parent->size) { tloge("offset + size exceed the parent size\n"); return (TEEC_Result)TEEC_ERROR_BAD_PARAMETERS; } } if (memref.parent->is_allocated) { if (!CheckSharedBufferExist(context, &memref)) { return (TEEC_Result)TEEC_ERROR_BAD_PARAMETERS; } } return (TEEC_Result)TEEC_SUCCESS; PARAM_ERROR: tloge("type of memref not belong to the parent flags\n"); return (TEEC_Result)TEEC_ERROR_BAD_PARAMETERS; } /* * Function: TEEC_CheckOperation * Description: This function checks an operation is valid or not. * Parameters: operation: a pointer to an Operation to be checked. * Return: TEEC_SUCCESS: success * other: failure */ TEEC_Result TEEC_CheckOperation(TEEC_ContextInner *context, const TEEC_Operation *operation) { uint32_t paramType[TEEC_PARAM_NUM]; uint32_t paramCnt; TEEC_Result ret = TEEC_SUCCESS; /* GP Support operation is NULL * operation: a pointer to a Client Application initialized TEEC_Operation structure, * or NULL if there is no payload to send or if the Command does not need to support * cancellation. */ if (operation == NULL) { return ret; } if (!operation->started) { tloge("sorry, cancellation not support\n"); return (TEEC_Result)TEEC_ERROR_NOT_IMPLEMENTED; } for (paramCnt = 0; paramCnt < TEEC_PARAM_NUM; paramCnt++) { paramType[paramCnt] = TEEC_PARAM_TYPE_GET(operation->paramTypes, paramCnt); bool checkValue = (paramType[paramCnt] == TEEC_ION_INPUT || paramType[paramCnt] == TEEC_ION_SGLIST_INPUT); if (IS_TEMP_MEM(paramType[paramCnt])) { ret = TEEC_CheckTmpRef(operation->params[paramCnt].tmpref); } else if (IS_PARTIAL_MEM(paramType[paramCnt])) { ret = TEEC_CheckMemRef(context, operation->params[paramCnt].memref, paramType[paramCnt]); } else if (IS_VALUE_MEM(paramType[paramCnt])) { /* if type is value, ignore it */ } else if (checkValue == true) { if (operation->params[paramCnt].ionref.ionShareFd < 0 || operation->params[paramCnt].ionref.ionSize == 0) { tloge("check failed: ion_share_fd and ion_size\n"); ret = (TEEC_Result)TEEC_ERROR_BAD_PARAMETERS; break; } } else if (paramType[paramCnt] == TEEC_NONE) { /* if type is none, ignore it */ } else { tloge("paramType is not support\n"); ret = (TEEC_Result)TEEC_ERROR_BAD_PARAMETERS; break; } if (ret != TEEC_SUCCESS) { tloge("paramCnt is %u\n", paramCnt); break; } } return ret; } /* * Function: TEEC_RequestCancellation * Description: This function requests the cancellation of a pending open Session operation or a Command invocation operation. * Parameters: operation:a pointer to a Client Application instantiated Operation structure * Return: void */ void TEEC_RequestCancellation(TEEC_Operation *operation) { int32_t ret; TEEC_Result teecRet; TC_NS_ClientContext cliContext; TC_NS_ClientLogin cliLogin = { 0, 0 }; /* First, check parameters is valid or not */ if (operation == NULL) { return; } TEEC_Session *session = operation->session; if ((session == NULL) || (session->context == NULL)) { tloge("session is invalid\n"); return; } TEEC_ContextInner *contextInner = GetBnContext(session->context); teecRet = TEEC_CheckOperation(contextInner, operation); (void)PutBnContext(contextInner); if (teecRet != TEEC_SUCCESS) { tloge("operation is invalid\n"); return; } /* Paramters all right, start execution */ teecRet = TEEC_Encode(&cliContext, session, TC_NS_CLIENT_IOCTL_CANCEL_CMD_REQ, &cliLogin, operation); if (teecRet != TEEC_SUCCESS) { tloge("RequestCancellation: teec encode failed(0x%x)!\n", teecRet); return; } ret = ioctl((int)session->context->fd, (int)TC_NS_CLIENT_IOCTL_CANCEL_CMD_REQ, &cliContext); if (ret == 0) { tlogd("invoke cmd success\n"); } else if (ret < 0) { tloge("invoke cmd failed, ioctl errno = %d\n", ret); } else { tloge("invoke cmd failed, code=0x%x, origin=%u\n", cliContext.returns.code, cliContext.returns.origin); } return; } #ifdef LIB_TEEC_VENDOR TEEC_Result TEEC_EXT_RegisterAgent(uint32_t agentId, int *devFd, void **buffer) { int ret; struct AgentIoctlArgs args = { 0 }; if ((devFd == NULL) || (buffer == NULL)) { tloge("Failed to open tee client dev!\n"); return (TEEC_Result)TEEC_ERROR_GENERIC; } int fd = CaDaemonConnectWithoutCaInfo(); if (fd < 0) { tloge("Failed to open tee client dev!\n"); return (TEEC_Result)TEEC_ERROR_GENERIC; } args.id = agentId; args.bufferSize = AGENT_BUFF_SIZE; ret = ioctl(fd, TC_NS_CLIENT_IOCTL_REGISTER_AGENT, &args); if (ret != 0) { (void)close(fd); tloge("ioctl failed, failed to register agent!\n"); return (TEEC_Result)TEEC_ERROR_GENERIC; } *devFd = fd; *buffer = args.buffer; return TEEC_SUCCESS; } TEEC_Result TEEC_EXT_WaitEvent(uint32_t agentId, int devFd) { int ret; ret = ioctl(devFd, TC_NS_CLIENT_IOCTL_WAIT_EVENT, agentId); if (ret != 0) { tloge("Agent 0x%x wait failed, errno=%d\n", agentId, ret); return TEEC_ERROR_GENERIC; } return TEEC_SUCCESS; } TEEC_Result TEEC_EXT_SendEventResponse(uint32_t agentId, int devFd) { int ret; ret = ioctl(devFd, TC_NS_CLIENT_IOCTL_SEND_EVENT_RESPONSE, agentId); if (ret != 0) { tloge("Agent %u failed to send response, ret is %d!\n", agentId, ret); return (TEEC_Result)TEEC_ERROR_GENERIC; } return TEEC_SUCCESS; } TEEC_Result TEEC_EXT_UnregisterAgent(uint32_t agentId, int devFd, void **buffer) { int ret; TEEC_Result result = TEEC_SUCCESS; if (buffer == NULL || *buffer == NULL) { tloge("buffer is invalid!\n"); return TEEC_ERROR_BAD_PARAMETERS; } if (devFd < 0) { tloge("fd is invalid!\n"); return TEEC_ERROR_BAD_PARAMETERS; } ret = ioctl(devFd, TC_NS_CLIENT_IOCTL_UNREGISTER_AGENT, agentId); if (ret != 0) { tloge("Failed to unregister agent %u, ret is %d\n", agentId, ret); result = TEEC_ERROR_GENERIC; } (void)close(devFd); *buffer = NULL; return result; } TEEC_Result TEEC_SendSecfile(const char *path, TEEC_Session *session) { TEEC_Result ret = (TEEC_Result)TEEC_SUCCESS; TEEC_ContextInner *contextInner = NULL; if (path == NULL || session == NULL || session->context == NULL) { tloge("params error!\n"); return TEEC_ERROR_BAD_PARAMETERS; } contextInner = GetBnContext(session->context); if (contextInner == NULL) { tloge("find context failed!\n"); return TEEC_ERROR_BAD_PARAMETERS; } ret = TEEC_SendSecfileInner(path, contextInner->fd, NULL); (void)PutBnContext(contextInner); return ret; } #endif TEEC_Result TEEC_SendSecfileInner(const char *path, int tzFd, FILE *fp) { int32_t ret; TEEC_Result teecRet = (TEEC_Result)TEEC_SUCCESS; if (path == NULL) { return TEEC_ERROR_BAD_PARAMETERS; } ret = TEEC_LoadSecfile(path, tzFd, fp); if (ret < 0) { tloge("Send secfile error\n"); teecRet = (TEEC_Result)TEEC_ERROR_TRUSTED_APP_LOAD_ERROR; } return teecRet; }