* Copyright (c) 2021 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "hdf_security.h"
#include <errno.h>
#include <fcntl.h>
#include <inttypes.h>
#include <openssl/rand.h>
#include <openssl/sha.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/time.h>
#include <unistd.h>
#include "hcs_tree_if.h"
#include "hdf_base.h"
#include "hdf_log.h"
#include "hdf_sec.h"
#include "pal_if.h"
#include "securec.h"
#define HDF_LOG_TAG hdf_security
#define HDF_SECURE_PATH "/dev/hdf_secure"
#define HDF_SEC_PERM_ELEM_NAME "permission"
#define HDF_SEC_HOST_NAME "hostName"
#define MAX_RANDOM_BYTE_LEN 8
#define RAND_OPS_SUCCESS 1
#define MAX_SEED_LEN 64
#define HDF_TRANSFORM_STEP 2
#define HDF_SECURE_INVALID_PERM 0
#define HDF_SECURE_SET_ALL_ID 0
#define HDF_SECURE_SET_CUR_ID 1
#define HDF_SECURE_DEL_CUR_ID 2
#define BITS_PER_LONG 64
#define BIT_MASK(nr) (1UL << ((nr) % BITS_PER_LONG))
#define BIT_WORD(nr) ((nr) / BITS_PER_LONG)
const static char *g_hdfSecStrArray[PAL_MAX_TYPE] = {
[PAL_I2C_TYPE] = "i2c",
[PAL_SPI_TYPE] = "spi",
[PAL_GPIO_TYPE] = "gpio",
[PAL_PINCTRL_TYPE] = "pinctl",
[PAL_CLOCK_TYPE] = "clock",
[PAL_REGULATOR_TYPE] = "regulator",
[PAL_MIPI_TYPE] = "mipi",
[PAL_UART_TYPE] = "uart",
[PAL_SDIO_TYPE] = "sdio",
[PAL_MDIO_TYPE] = "mdio",
[PAL_APB_TYPE] = "apb",
[PAL_PCIE_TYPE] = "pcie",
[PAL_PCM_TYPE] = "pcm",
[PAL_I2S_TYPE] = "i2s",
[PAL_PWM_TYPE] = "pwm",
[PAL_DMA_TYPE] = "dma",
[PAL_EFUSE_TYPE] = "efuse",
[PAL_FLASH_TYPE] = "flash",
[PAL_EMMC_TYPE] = "emmc",
[PAL_RTC_TYPE] = "rtc",
[PAL_ADC_TYPE] = "adc",
[PAL_WDT_TYPE] = "wdt",
[PAL_I3C_TYPE] = "i3c",
};
static Map g_indexMap;
Map *HdfSecGetHashMap()
{
static Map hdfHashMap = { 0 };
if (hdfHashMap.nodeSize == 0) {
HDF_LOGI("Init hdfHashMap");
MapInit(&hdfHashMap);
}
return &hdfHashMap;
}
static void InitStrToIndexMap()
{
MapInit(&g_indexMap);
for (int i = 0; i < PAL_MAX_TYPE; ++i) {
MapSet(&g_indexMap, g_hdfSecStrArray[i], &i, sizeof(int *));
}
}
static void HdfSetBit(uint32_t nr, volatile uint64_t *addr)
{
unsigned long mask = BIT_MASK(nr);
unsigned long *p = ((unsigned long *)addr) + BIT_WORD(nr);
*p |= mask;
}
static int32_t HdfGetRandom(int numBytes, uint8_t *random)
{
if (RAND_bytes(random, numBytes) != RAND_OPS_SUCCESS) {
HDF_LOGE("%{public}s: RAND_bytes failed", __FUNCTION__);
return HDF_FAILURE;
}
return HDF_SUCCESS;
}
static void HdfCalculatePerms(const char *permStr, volatile uint64_t *perms)
{
uint32_t *result = NULL;
result = (uint32_t *)MapGet(&g_indexMap, permStr);
if (result == NULL) {
HDF_LOGE("%{public}s: %{public}s is not exist in map", __FUNCTION__, permStr);
return;
}
HdfSetBit(*result, perms);
}
static int32_t HdfSecCheckParameters(const char *id)
{
if (id == NULL) {
HDF_LOGE("HdfSecCheckParameters failed, id is null");
return HDF_FAILURE;
}
uint32_t len = (uint32_t)strlen(id);
if (len >= (ID_MAX_SIZE - 1)) {
HDF_LOGE("HdfSecCheckParameters failed, id length is %{public}u", len);
return HDF_FAILURE;
}
return HDF_SUCCESS;
}
static int32_t HdfUpdateSecurityId(const char *id, const uint64_t perms, const int32_t isSetCurrentSecId)
{
if (HdfSecCheckParameters(id) != HDF_SUCCESS) {
HDF_LOGE("%{public}s check id failed", __FUNCTION__);
return HDF_FAILURE;
}
struct SecInfo secInfo = {
.secId = { 0 },
.secMap = { 0 }
};
int32_t ret = strncpy_s(secInfo.secId, ID_MAX_SIZE, id, ID_MAX_SIZE - 1);
if (ret != EOK) {
HDF_LOGE("%{public}s strncpy_s id failed", __FUNCTION__);
return HDF_FAILURE;
}
secInfo.secMap[0] = perms;
char path[PATH_MAX + 1] = { 0 };
if (realpath(HDF_SECURE_PATH, path) == NULL) {
HDF_LOGE("file %{public}s is invalid", HDF_SECURE_PATH);
return HDF_FAILURE;
}
int32_t fd = open(path, O_WRONLY);
if (fd < 0) {
HDF_LOGE("open %{public}s failed, errno is %{public}d", HDF_SECURE_PATH, errno);
return HDF_FAILURE;
}
if (isSetCurrentSecId == 0) {
ret = ioctl(fd, HDF_SECURE_SET_INFO, &secInfo);
} else if (isSetCurrentSecId == 1) {
ret = ioctl(fd, HDF_SECURE_SET_CURRENT_ID, &secInfo);
} else {
ret = ioctl(fd, HDF_SECURE_DELETE_INFO, &secInfo);
}
close(fd);
fd = -1;
if (ret != 0) {
HDF_LOGE("%{public}s ioctl error, ret is %{public}d", __FUNCTION__, ret);
return HDF_FAILURE;
}
return HDF_SUCCESS;
}
static int32_t HdfRegisterSecurityId(const char *id, const uint64_t perm)
{
return HdfUpdateSecurityId(id, perm, HDF_SECURE_SET_ALL_ID);
}
int32_t HdfRegisterAllDevSecId(const struct DeviceResourceNode *hostRoot)
{
if (hostRoot == NULL) {
HDF_LOGE("device_node linked list completed");
return HDF_FAILURE;
}
uint64_t random = 0;
int32_t ret = HdfGetRandom(MAX_RANDOM_BYTE_LEN, (uint8_t *)&random);
if (ret != HDF_SUCCESS) {
HDF_LOGE("%{public}s HdfGetRandom failed", __FUNCTION__);
return HDF_FAILURE;
}
InitStrToIndexMap();
struct DeviceResourceNode *driverRoot = hostRoot->child;
const char *permStr = NULL;
volatile uint64_t perms = HDF_SECURE_INVALID_PERM;
while (driverRoot != NULL) {
const struct DeviceResourceNode *deviceRoot = driverRoot->child;
while (deviceRoot != NULL) {
int32_t permNums = HcsGetElemNum(deviceRoot, HDF_SEC_PERM_ELEM_NAME);
HDF_LOGD("name is %{public}s, permNums is %{public}d", deviceRoot->name, permNums);
for (int i = 0; i < permNums; ++i) {
HcsGetStringArrayElem(deviceRoot, HDF_SEC_PERM_ELEM_NAME, i, &permStr, NULL);
HdfCalculatePerms(permStr, &perms);
}
deviceRoot = deviceRoot->sibling;
}
driverRoot = driverRoot->sibling;
}
if (perms != HDF_SECURE_INVALID_PERM) {
const char *hostName = NULL;
HcsGetString(hostRoot, HDF_SEC_HOST_NAME, &hostName, NULL);
ret = HdfRegisterSecurityId(hostName, perms);
if (ret != HDF_SUCCESS) {
HDF_LOGE("HdfCreateSecurityId failed for %{public}s", hostRoot->name);
}
perms = HDF_SECURE_INVALID_PERM;
}
return HDF_SUCCESS;
}
int32_t HdfSetCurrentHostSecurity(const char *hostName, int32_t procId)
{
return HdfUpdateSecurityId(hostName, procId, HDF_SECURE_SET_CUR_ID);
}
void HdfDelSecurity(const char *hostName)
{
if (hostName == NULL) {
return;
}
int32_t ret = HdfUpdateSecurityId(hostName, 0, HDF_SECURE_DEL_CUR_ID);
if (ret != HDF_SUCCESS) {
HDF_LOGE("%{public}s delete %{public}s security failed", __func__, hostName);
}
}