* Copyright (c) Huawei Technologies Co., Ltd. 2018-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_auth_common.h"
#include <stdbool.h>
#include <errno.h>
#include "tee_client_constants.h"
#include "securec.h"
#include "tee_log.h"
#ifdef LOG_TAG
#undef LOG_TAG
#endif
#define LOG_TAG "teecd_auth"
#define DELIM_COUNT 6U
#define NAME_POS 0U
#define UID_POS 1U
#define DECIMAL 10
static int UidCompare(unsigned int caUid, const char *uidString, size_t uidLen)
{
size_t i = 0;
size_t uidNum = 0;
for (; i < uidLen; i++) {
bool is_number = uidString[i] >= '0' && uidString[i] <= '9';
if (!is_number) {
tloge("passwd info wrong format: uid missing\n");
return -1;
}
uidNum = DECIMAL * uidNum + (size_t)(uidString[i] - '0');
}
if (uidNum == caUid) {
return 0;
}
return -1;
}
static int SplitFields(int delimIndices[], const char *userName, int nameBufLen)
{
int i = 0;
unsigned int count = 0;
for (; i < nameBufLen && userName[i] != '\0'; i++) {
if (count > DELIM_COUNT) {
tloge("passwd info wrong format: extra field\n");
return -1;
}
if (userName[i] == ':') {
delimIndices[count] = i;
count++;
}
}
return 0;
}
static int ParseUserName(unsigned int caUid, const char *userName, int nameBufLen)
{
int delimIndices[DELIM_COUNT + 1] = { 0 };
int ret = SplitFields(delimIndices, userName, nameBufLen);
if (ret != 0) {
return ret;
}
const char *uidString = userName + delimIndices[UID_POS] + 1;
int uidLen = delimIndices[UID_POS + 1] - delimIndices[UID_POS] - 1;
if (UidCompare(caUid, uidString, uidLen) == 0) {
return delimIndices[NAME_POS];
}
return -1;
}
* on linux, user info is stored in system file "/proc/<pid>/root/etc/passwd",
* each line represents a user, fields are separated by ':',
* formatted as such: "username:[encrypted password]:uid:gid:[comments]:home directory:login shell"
*/
int TeeGetUserName(int caPid, unsigned int caUid, char *userName, size_t nameBufLen)
{
int i;
char path[MAX_PATH_LENGTH] = { 0 };
int ret = snprintf_s(path, sizeof(path), sizeof(path) - 1, "/proc/%d/root/etc/passwd", caPid);
if (ret == -1) {
tloge("get passwd filename failed\n");
return -1;
}
FILE *fd = fopen(path, "r");
if (fd == NULL) {
tloge("open passwd file failed\n");
return -1;
}
while (fgets(userName, nameBufLen - 1, fd) != NULL) {
int userNameLen = ParseUserName(caUid, userName, (int)nameBufLen);
if (userNameLen != -1) {
for (i = userNameLen; i < (int)nameBufLen; i++) {
userName[i] = '\0';
}
fclose(fd);
return 0;
}
}
(void)fclose(fd);
return -1;
}
static int LocatePkgName(const char *cmdLine, size_t cmdLen)
{
int rIndex = (int)cmdLen - 1;
int nextArgStart = -1;
char *target_str = "-jar";
int target_len = strlen(target_str);
while (rIndex >= 0) {
while (rIndex >= 0 && cmdLine[rIndex] == '\0') {
rIndex--;
}
if (rIndex < 0)
break;
int argEnd = rIndex;
while (rIndex >= 0 && cmdLine[rIndex] != '\0') {
rIndex--;
}
int argStart = rIndex + 1;
int argLen = argEnd - argStart + 1;
if (argLen == target_len && memcmp(cmdLine + argStart, target_str, target_len) == 0) {
return nextArgStart;
}
nextArgStart = argStart;
}
return -1;
}
* cmdline format looks like this:
* "java option1 option2 ... optionn -jar com.company.module.app"
* we need to extract the package name "com.company.module.app" from it
*/
#define JAVA_CMD_MIN_INDEX 3
#define JAVA_OFFSET_3 3
#define JAVA_OFFSET_2 2
#define JAVA_OFFSET_1 1
static int ParsePkgName(const char *cmdLine, size_t cmdLen, char *caName, size_t nameLen)
{
int rIndex = (int)strnlen(caName, nameLen) - 1;
bool is_java_cmd = rIndex >= JAVA_CMD_MIN_INDEX &&
caName[rIndex - JAVA_OFFSET_3] == 'j' &&
caName[rIndex - JAVA_OFFSET_2] == 'a' &&
caName[rIndex - JAVA_OFFSET_1] == 'v' &&
caName[rIndex] == 'a';
if (!is_java_cmd) {
return 0;
}
rIndex = LocatePkgName(cmdLine, cmdLen);
if (rIndex < 0) {
tlogw("locate pkg name failed,use default caname:%s.\n", caName);
return 0;
}
int pkgLen = strnlen(cmdLine + rIndex, cmdLen - rIndex);
errno_t ret = strncpy_s(caName, nameLen - 1, cmdLine + rIndex, pkgLen);
if (ret != EOK) {
tloge("copy caName failed\n");
return -1;
}
for (; pkgLen < (int)nameLen; pkgLen++) {
caName[pkgLen] = '\0';
}
return 0;
}
static int ReadCmdLine(const char *path, char *buffer, size_t bufferLen, char *caName, size_t nameLen)
{
FILE *fd = fopen(path, "rb");
if (fd == NULL) {
tloge("fopen is error: %d\n", errno);
return -1;
}
int bytesRead = (int)fread(buffer, sizeof(char), bufferLen - 1, fd);
bool readError = (bytesRead <= 0 || (ferror(fd) != 0));
if (readError) {
tloge("cannot read from cmdline\n");
fclose(fd);
return -1;
}
(void)fclose(fd);
if (caName == NULL || nameLen == 0)
return bytesRead;
size_t firstStringLen = strnlen(buffer, bufferLen - 1);
errno_t res = strncpy_s(caName, nameLen - 1, buffer, firstStringLen);
if (res != EOK) {
tloge("copy caName failed\n");
return -1;
}
return bytesRead;
}
* this file "/proc/pid/cmdline" can be modified by any user,
* so the package name we get from it is not to be trusted,
* the CA authentication strategy does not rely much on the pkgname,
* this is mainly to make it compatible with POHNE_PLATFORM
*/
static int TeeGetCaName(int caPid, char *caName, size_t nameLen)
{
char path[MAX_PATH_LENGTH] = { 0 };
char temp[CMD_MAX_SIZE] = { 0 };
if (caName == NULL || nameLen == 0) {
tloge("input :caName invalid\n");
return -1;
}
int ret = snprintf_s(path, sizeof(path), sizeof(path) - 1, "/proc/%d/cmdline", caPid);
if (ret == -1) {
tloge("tee get ca name snprintf_s err\n");
return ret;
}
int bytesRead = ReadCmdLine(path, temp, CMD_MAX_SIZE, caName, nameLen);
bool stat = bytesRead <= 0 || ParsePkgName(temp, bytesRead, caName, nameLen) != 0;
if (stat) {
tloge("parse package name from cmdline failed\n");
return -1;
}
return bytesRead;
}
int TeeGetPkgName(int caPid, char *path, size_t pathLen)
{
if (path == NULL || pathLen > MAX_PATH_LENGTH) {
tloge("path is null or path len overflows\n");
return -1;
}
if (TeeGetCaName(caPid, path, pathLen) < 0) {
tloge("get ca name failed\n");
return -1;
}
if (strncmp(path, MEDIA_CODEC_PATH, strlen(MEDIA_CODEC_PATH) + 1) == 0) {
int ret = snprintf_s(path, pathLen, strlen(OMX_PATH), OMX_PATH);
if (ret < 0) {
tloge("copy omx path failed");
return ret;
}
}
return 0;
}
#ifdef CONFIG_TA_GET_CA_CMDLINE
static void parse_cmdline(char *str, int len)
{
for (int i = 0; i < len - 1; ++i) {
if (str[i] == 0) {
str[i] = ' ';
}
}
}
int TeeGetCaCmdline(int caPid, uint8_t *buffer, uint32_t *len)
{
char path[MAX_PATH_LENGTH] = { 0 };
char cmdline[MAX_CMDLINE_LENGTH] = { 0 };
uint32_t freeLen = 0;
if (buffer == NULL || len == NULL || *len < MAX_CMDLINE_LENGTH + sizeof(uint32_t)) {
tloge("buffer is null or len invalid\n");
return -1;
}
freeLen = *len;
int ret = snprintf_s(path, sizeof(path), sizeof(path) - 1, "/proc/%d/cmdline", caPid);
if (ret == -1) {
tloge("tee get ca cmdline snprintf_s err %d\n", ret);
return ret;
}
int bytesRead = ReadCmdLine(path, cmdline, MAX_CMDLINE_LENGTH, NULL, 0);
if (bytesRead <= 0) {
tloge("tee get ca cmdline err\n");
return -1;
}
parse_cmdline(cmdline, bytesRead);
ret = memcpy_s(buffer, freeLen, &bytesRead, sizeof(uint32_t));
if (ret != EOK) {
tloge("copy cmdline length failed, err %d\n", ret);
return ret;
}
buffer += sizeof(uint32_t);
freeLen -= (uint32_t)sizeof(bytesRead);
ret = memcpy_s(buffer, freeLen, &cmdline, bytesRead);
if (ret != EOK) {
tloge("copy cmdline failed, err %d\n", ret);
return ret;
}
*len = (uint32_t)bytesRead + (uint32_t)sizeof(bytesRead);
tlogd("get ca cmdline len %d, str: %s\n", bytesRead, (char *)cmdline);
return 0;
}
#endif
static int TeeCheckCaPath(unsigned int uid, int pid, const char *auth_ctx)
{
char path[MAX_PATH_LENGTH] = { 0 };
char str_path_uid[BUF_MAX_SIZE] = { 0 };
if (auth_ctx == NULL) {
tloge("bad params\n");
return TEEC_ERROR_ACCESS_DENIED;
}
size_t auth_ctx_len = strnlen(auth_ctx, MAX_PATH_LENGTH);
if (auth_ctx_len == 0 || auth_ctx_len >= MAX_PATH_LENGTH) {
tloge("invalid path context\n");
return TEEC_ERROR_ACCESS_DENIED;
}
if (TeeGetCaName(pid, path, sizeof(path)) < 0) {
tloge("get ca name failed\n");
return TEEC_ERROR_ACCESS_DENIED;
}
if (snprintf_s((char *)str_path_uid, sizeof(str_path_uid), sizeof(str_path_uid) - 1, "%s:%u", path, uid) == -1) {
tloge("snprintf_s failed!\n");
return TEEC_ERROR_ACCESS_DENIED;
}
if (strnlen(str_path_uid, BUF_MAX_SIZE) != auth_ctx_len || strncmp(str_path_uid, auth_ctx, auth_ctx_len) != 0) {
tloge("check path failed\n");
return TEEC_ERROR_ACCESS_DENIED;
}
return 0;
}
int TeeCheckHidlAuth(unsigned int uid, int pid)
{
int ret = TeeCheckCaPath(uid, pid, CA_HIDL_PATH_UID_AUTH_CTX);
if (ret != 0) {
tloge("check hidl path failed, ret %d\n", ret);
return ret;
}
return 0;
}