* Copyright (c) Huawei Technologies Co., Ltd. 2020-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_socket.h"
#include <errno.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <securec.h>
#include <time.h>
#include "tee_log.h"
#include "tc_ns_client.h"
#include "tee_client_version.h"
#include "tee_client_inner.h"
#ifdef LOG_TAG
#undef LOG_TAG
#endif
#define LOG_TAG "libteec_vendor"
#define EACCES_ERR (-3)
#define SEND_MESS_ERR (-4)
#define IOV_LEN 1
static bool g_firstConnectTeecd = true;
static int g_teecVersionCheckResult = -1;
static int ConnectTeecdSocket(int *socketFd)
{
int ret;
uint32_t len;
struct sockaddr_un remote;
if (socketFd == NULL) {
tloge("bad parameter\n");
return -1;
}
errno_t rc = memset_s(&remote, sizeof(remote), 0, sizeof(remote));
if (rc != EOK) {
return -1;
}
int s = socket(AF_UNIX, SOCK_STREAM, 0);
if (s == -1) {
tloge("can't open stream socket, errno=%d", errno);
return -1;
}
tlogd("Trying to connect...\n");
remote.sun_family = AF_UNIX;
rc = strncpy_s(remote.sun_path, sizeof(remote.sun_path), TC_NS_SOCKET_NAME, sizeof(TC_NS_SOCKET_NAME));
if (rc != EOK) {
tloge("strncpy_s failed, errno=%d", rc);
close(s);
return -1;
}
len = (uint32_t)(strlen(remote.sun_path) + sizeof(remote.sun_family));
#ifndef CONFIG_PATH_NAMED_SOCKET
remote.sun_path[0] = 0;
#endif
if (connect(s, (struct sockaddr *)&remote, len) == -1) {
tloge("connect() failed, errno=%d\n", errno);
ret = -1;
if (errno == EACCES) {
ret = EACCES_ERR;
}
close(s);
return ret;
}
tlogd("Connected.\n");
*socketFd = s;
return 0;
}
static int InitRecvMsg(struct msghdr *recvMsg, struct iovec *iov, size_t iovLen,
char *ctrlBuf, size_t ctrlBufLen)
{
if (recvMsg == NULL || iov == NULL || ctrlBuf == NULL) {
tloge("param error!\n");
return EINVAL;
}
recvMsg->msg_iov = iov;
recvMsg->msg_iovlen = iovLen;
recvMsg->msg_name = NULL;
recvMsg->msg_namelen = 0;
recvMsg->msg_control = ctrlBuf;
recvMsg->msg_controllen = ctrlBufLen;
return EOK;
}
static int CheckTeecdVersion(const RecvTeecdMsg *msg)
{
const uint16_t teecMajorVersion = TEEC_CLIENT_VERSION_MAJOR_SELF;
const uint16_t teecMinorVersion = TEEC_CLIENT_VERSION_MINOR_SELF;
if (msg->majorVersion != teecMajorVersion) {
tloge("check major version failed, libteec major version %u, teecd major version %u\n",
teecMajorVersion, msg->majorVersion);
return -1;
}
if (teecMinorVersion > msg->minorVersion) {
tloge("check minor version failed, libteec version %u.%u, teecd version %u.%u\n",
teecMajorVersion, teecMinorVersion, msg->majorVersion, msg->minorVersion);
return -1;
} else if (teecMinorVersion < msg->minorVersion) {
tlogi("current libteec version %u.%u, teecd version %u.%u\n",
teecMajorVersion, teecMinorVersion, msg->majorVersion, msg->minorVersion);
}
return 0;
}
static int RecvSockMsg(int cmd, int socketFd)
{
struct msghdr hmsg;
struct iovec iov[IOV_LEN];
struct cmsghdr *controlMsg = NULL;
char ctrlBuf[CMSG_SPACE(sizeof(int))];
RecvTeecdMsg data = { 0 };
ssize_t res;
errno_t rc;
int *cmdata = NULL;
rc = memset_s(&hmsg, sizeof(struct msghdr), 0, sizeof(struct msghdr));
if (rc != EOK) {
return -1;
}
rc = memset_s(ctrlBuf, sizeof(ctrlBuf), 0, CMSG_SPACE(sizeof(int)));
if (rc != EOK) {
return -1;
}
iov[0].iov_base = &data;
iov[0].iov_len = sizeof(data);
rc = InitRecvMsg(&hmsg, iov, IOV_LEN, ctrlBuf, CMSG_SPACE(sizeof(int)));
if (rc != EOK) {
tloge("init msg failed!\n");
return -1;
}
if (cmd == GET_TEEVERSION || cmd == GET_TEECD_VERSION) {
hmsg.msg_control = NULL;
hmsg.msg_controllen = 0;
res = recvmsg(socketFd, &hmsg, 0);
if (res <= 0) {
return -1;
}
if (cmd == GET_TEEVERSION) {
return data.teeMaxApiLevel;
} else {
g_teecVersionCheckResult = CheckTeecdVersion(&data);
return 0;
}
}
res = recvmsg(socketFd, &hmsg, 0);
if (res <= 0) {
return -1;
}
for (controlMsg = CMSG_FIRSTHDR(&hmsg); controlMsg != NULL; controlMsg = CMSG_NXTHDR(&hmsg, controlMsg)) {
if ((controlMsg->cmsg_level == SOL_SOCKET) && (controlMsg->cmsg_type == SCM_RIGHTS)) {
cmdata = (int *)(uintptr_t)CMSG_DATA(controlMsg);
return *cmdata;
}
}
return -1;
}
static int FillMsgBuffer(const CaAuthInfo *caInfo, CaRevMsg **revMsg, int cmd, const TEEC_XmlParameter *halXmlPtr)
{
CaRevMsg *revBuffer = (CaRevMsg *)malloc(sizeof(*revBuffer));
if (revBuffer == NULL) {
tloge("alloc mem failed.\n");
return (int)TEEC_ERROR_OUT_OF_MEMORY;
}
errno_t ret = memset_s((void *)revBuffer, sizeof(*revBuffer), 0, sizeof(*revBuffer));
if (ret != EOK) {
tloge("memset failed\n");
free(revBuffer);
return -1;
}
int temp = memcpy_s(&(revBuffer->caAuthInfo), sizeof(CaAuthInfo), caInfo, sizeof(*caInfo));
if (temp != EOK) {
tloge("memcpy_s error!\n");
free(revBuffer);
return -1;
}
revBuffer->cmd = cmd;
if (cmd == SET_SYS_XML && halXmlPtr != NULL) {
revBuffer->xmlBufSize = halXmlPtr->fileSize;
temp = memcpy_s(revBuffer->xmlBuffer, HASH_FILE_MAX_SIZE, halXmlPtr->fileBuf, halXmlPtr->fileSize);
if (temp != EOK) {
tloge("memcpy_s error!\n");
free(revBuffer);
return -1;
}
}
*revMsg = revBuffer;
return EOK;
}
static void InitSockMsg(struct msghdr *message, CaRevMsg *revMsg, struct iovec *iov)
{
message->msg_name = NULL;
message->msg_namelen = 0;
message->msg_flags = 0;
message->msg_iov = iov;
message->msg_iovlen = 1;
message->msg_control = NULL;
message->msg_controllen = 0;
(message->msg_iov[0]).iov_base = revMsg;
(message->msg_iov[0]).iov_len = sizeof(*revMsg);
}
static void SleepNs(long num)
{
struct timespec ts;
ts.tv_sec = 0;
ts.tv_nsec = num;
if (nanosleep(&ts, NULL) != 0) {
tlogd("nanosleep ms error\n");
}
}
#define SLEEP_TIME (200 * 1000 * 1000)
#define SLEEP_COUNT 50
static int CaDaemonConnect(const CaAuthInfo *caInfo, int cmd, const TEEC_XmlParameter *halXmlPtr)
{
int s = -1;
int32_t failCount = 0;
int32_t cRet;
struct msghdr message;
struct iovec iov[1];
CaRevMsg *revMsg = NULL;
int32_t msgRet = -1;
while (failCount++ < SLEEP_COUNT) {
cRet = ConnectTeecdSocket(&s);
if (cRet != -1) {
break;
}
tlogd("open device failed, retry!\n");
SleepNs(SLEEP_TIME);
}
if (cRet < 0) {
tloge("try connect ca daemon failed, fail_counts = %d\n", failCount);
return -1;
}
if (memset_s(&message, sizeof(message), 0, sizeof(message)) != EOK) {
tloge("ca daemon: memset failed\n");
close(s);
return -1;
}
if (FillMsgBuffer(caInfo, &revMsg, cmd, halXmlPtr) != EOK) {
tloge("memcpy_s error!\n");
close(s);
return -1;
}
InitSockMsg(&message, revMsg, iov);
if (sendmsg(s, &message, 0) < 0) {
tloge("send message error %d \n", errno);
close(s);
free(revMsg);
return SEND_MESS_ERR;
}
msgRet = RecvSockMsg(cmd, s);
close(s);
free(revMsg);
return msgRet;
}
int CaDaemonConnectWithCaInfo(const CaAuthInfo *caInfo, int cmd, const TEEC_XmlParameter *halXmlPtr)
{
if (caInfo == NULL) {
tloge("ca daemon: ca auth info is NULL\n");
return -1;
}
if (g_firstConnectTeecd) {
if (CaDaemonConnect(caInfo, GET_TEECD_VERSION, halXmlPtr) != 0) {
tloge("get teecd version failed\n");
return -1;
}
g_firstConnectTeecd = false;
}
if (g_teecVersionCheckResult != 0) {
tloge("check teecd version failed\n");
return -1;
}
int fd = CaDaemonConnect(caInfo, cmd, halXmlPtr);
if (cmd == GET_FD && fd >= 0) {
tlogd("Fd received!\n");
}
return fd;
}