/* * Copyright (c) 2022 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 "dslm_fsm_process.h" #include #include #include #include "device_security_defines.h" #include "utils_datetime.h" #include "utils_dslm_list.h" #include "utils_hexstring.h" #include "utils_log.h" #include "utils_mem.h" #include "utils_mutex.h" #include "utils_state_machine.h" #include "utils_timer.h" #include "dslm_callback_info.h" #include "dslm_core_defines.h" #include "dslm_cred.h" #include "dslm_device_list.h" #include "dslm_hitrace.h" #include "dslm_inner_process.h" #include "dslm_msg_serialize.h" #include "dslm_notify_node.h" #define REQUEST_INTERVAL (24 * 60 * 60 * 1000) #define DEFAULT_TYPE 10 #define TYPE_PLACE 8 typedef bool DslmInfoChecker(const DslmDeviceInfo *devInfo, const DslmNotifyListNode *node, DslmCallbackInfo *cbInfo, uint32_t *result); static bool SdkTimeoutChecker(const DslmDeviceInfo *devInfo, const DslmNotifyListNode *node, DslmCallbackInfo *cbInfo, uint32_t *result); static bool RequestDoneChecker(const DslmDeviceInfo *devInfo, const DslmNotifyListNode *node, DslmCallbackInfo *cbInfo, uint32_t *result); static uint32_t GenerateMachineId(const DeviceIdentify *identity); static bool CheckTimesAndSendCredRequest(DslmDeviceInfo *info, bool enforce); static void StopSendDeviceInfoRequestTimer(DslmDeviceInfo *info); static void ProcessSendDeviceInfoCallback(DslmDeviceInfo *info, DslmInfoChecker checker); static void TimerProcessSendDeviceInfoRequestTimeOut(const void *context); static void TimerProcessSdkRequestTimeout(const void *context); static bool ProcessDeviceOnline(const StateMachine *machine, uint32_t event, const void *para); static bool ProcessSendCredRequest(const StateMachine *machine, uint32_t event, const void *para); static bool ProcessSdkRequest(const StateMachine *machine, uint32_t event, const void *para); static bool ProcessSendRequestFailed(const StateMachine *machine, uint32_t event, const void *para); static bool ProcessDeviceOffline(const StateMachine *machine, uint32_t event, const void *para); static bool ProcessVerifyCredMessage(const StateMachine *machine, uint32_t event, const void *para); static bool ProcessSdkTimeout(const StateMachine *machine, uint32_t event, const void *para); static void RefreshNotifyList(DslmDeviceInfo *info); static void RefreshHistoryList(DslmDeviceInfo *info); static uint32_t GenerateMachineId(const DeviceIdentify *identity) { #define MASK_LOW 0x00ffU #define MACHINE_ID_LENGTH 4U #define SHIFT_LENGTH 8U #define MASK_HIGH 0xff00U uint16_t machineId = 0; DslmHexStringToByte((const char *)identity->identity, MACHINE_ID_LENGTH, (uint8_t *)&machineId, sizeof(machineId)); return ((machineId & MASK_HIGH) >> SHIFT_LENGTH) | ((machineId & MASK_LOW) << SHIFT_LENGTH); } static void TimerProcessSendDeviceInfoRequestTimeOut(const void *context) { if (context == NULL) { return; } // the context info will never be freed, so feel free use it. ScheduleDslmStateMachine((DslmDeviceInfo *)context, EVENT_TIME_OUT, NULL); } static void TimerProcessSdkRequestTimeout(const void *context) { if (context == NULL) { return; } // the context info will never be freed, so feel free use it. ScheduleDslmStateMachine((DslmDeviceInfo *)context, EVENT_SDK_TIMEOUT, NULL); } static void StopSendDeviceInfoRequestTimer(DslmDeviceInfo *info) { if (info->timeHandle != 0) { DslmUtilsStopTimerTask(info->timeHandle); info->timeHandle = 0; } } static void StartSendDeviceInfoRequestTimer(DslmDeviceInfo *info) { info->timeHandle = DslmUtilsStartOnceTimerTask(SEND_MSG_TIMEOUT_LEN, TimerProcessSendDeviceInfoRequestTimeOut, info); } static bool CheckTimesAndSendCredRequest(DslmDeviceInfo *info, bool enforce) { #ifndef MAX_SEND_TIMES #define MAX_SEND_TIMES 5 #endif #ifndef SEND_MSG_TIMEOUT_LEN #define SEND_MSG_TIMEOUT_LEN 40000 #endif if (!enforce && info->queryTimes > MAX_SEND_TIMES) { return false; } DslmStartProcessTraceAsync("SendCredRequest", info->machine.machineId, info->queryTimes + 1); CheckAndGenerateChallenge(info); SendDeviceInfoRequest(info); info->queryTimes++; info->lastRequestTime = GetMillisecondSinceBoot(); StopSendDeviceInfoRequestTimer(info); StartSendDeviceInfoRequestTimer(info); return true; } static void ProcessSendDeviceInfoCallback(DslmDeviceInfo *info, DslmInfoChecker checker) { #ifndef MAX_HISTORY_CNT #define MAX_HISTORY_CNT 30U #endif if (info == NULL || checker == NULL) { return; } ListNode *node = NULL; ListNode *temp = NULL; SECURITY_LOG_DEBUG("ProcessSendDeviceInfoCallback for device %{public}x.", info->machine.machineId); FOREACH_LIST_NODE_SAFE (node, &info->notifyList, temp) { DslmNotifyListNode *notifyNode = LIST_ENTRY(node, DslmNotifyListNode, linkNode); uint32_t result; DslmCallbackInfo cbInfo; bool check = checker(info, notifyNode, &cbInfo, &result); if (!check) { continue; } SECURITY_LOG_DEBUG("ProcessSendDeviceInfoCallback result %{public}u for device %{public}x, level %{public}u.", result, info->machine.machineId, cbInfo.level); notifyNode->requestCallback(notifyNode->owner, notifyNode->cookie, result, &cbInfo); notifyNode->stop = GetMillisecondSinceBoot(); notifyNode->result = result; RemoveListNode(node); DslmFinishProcessTraceAsync("SDK_GET", notifyNode->owner, notifyNode->cookie); AddListNodeBefore(node, &info->historyList); } RefreshNotifyList(info); RefreshHistoryList(info); } static bool CheckNeedToResend(const DslmDeviceInfo *info) { if (info->credInfo.credLevel > 0) { return false; } if (info->credInfo.credLevel == 0) { return true; } if (info->lastOnlineTime < info->lastRequestTime) { return true; } if (info->lastOnlineTime - info->lastRequestTime > (uint64_t)REQUEST_INTERVAL) { return true; } return false; } static bool ProcessDeviceOnline(const StateMachine *machine, uint32_t event, const void *para) { DslmDeviceInfo *info = STATE_MACHINE_ENTRY(machine, DslmDeviceInfo, machine); uint32_t deviceAttributes = 0; if (para != NULL) { deviceAttributes = *(uint32_t *)para; } uint32_t level = deviceAttributes & 0xFF; uint32_t osType = (deviceAttributes & 0xFF00) >> TYPE_PLACE; info->osType = osType; if (level == 0 && osType == DEFAULT_TYPE) { level = 1; SECURITY_LOG_INFO("level set 1"); } if (level > 0) { info->credInfo.credLevel = level; info->result = SUCCESS; } info->onlineStatus = ONLINE_STATUS_ONLINE; info->queryTimes = 0; info->lastOnlineTime = GetMillisecondSinceBoot(); if (!CheckNeedToResend(info)) { SECURITY_LOG_INFO("last request time is last than 24 hours"); ScheduleDslmStateMachine(info, EVENT_TO_SYNC, NULL); return true; } return ProcessSendCredRequest(machine, event, para); } static bool ProcessSendCredRequest(const StateMachine *machine, uint32_t event, const void *para) { DslmDeviceInfo *info = STATE_MACHINE_ENTRY(machine, DslmDeviceInfo, machine); bool enforce = (para != NULL); return CheckTimesAndSendCredRequest(info, enforce); } static bool ProcessSdkRequest(const StateMachine *machine, uint32_t event, const void *para) { DslmDeviceInfo *deviceInfo = STATE_MACHINE_ENTRY(machine, DslmDeviceInfo, machine); DslmNotifyListNode *inputNotify = (DslmNotifyListNode *)para; if (inputNotify == NULL) { return false; } DslmNotifyListNode *notify = MALLOC(sizeof(DslmNotifyListNode)); if (notify == NULL) { SECURITY_LOG_ERROR("malloc failed, notifyNode is null"); return false; } (void)memset_s(notify, sizeof(DslmNotifyListNode), 0, sizeof(DslmNotifyListNode)); notify->owner = inputNotify->owner; notify->cookie = inputNotify->cookie; notify->requestCallback = inputNotify->requestCallback; notify->start = inputNotify->start; notify->keep = inputNotify->keep; if (notify->cookie == 0 || notify->requestCallback == NULL) { SECURITY_LOG_ERROR("ProcessSdkRequest invalid cookie or callback."); FREE(notify); notify = NULL; return false; } DslmStartProcessTraceAsync("SDK_GET", notify->owner, notify->cookie); AddListNode(¬ify->linkNode, &deviceInfo->notifyList); RefreshNotifyList(deviceInfo); SECURITY_LOG_DEBUG( "ProcessSdkRequest, device is %{public}x, owner is %{public}u, cookie is %{public}u, keep is %{public}u", deviceInfo->machine.machineId, notify->owner, notify->cookie, notify->keep); uint32_t state = GetCurrentMachineState(deviceInfo); if (state == STATE_SUCCESS || state == STATE_FAILED || deviceInfo->credInfo.credLevel != 0) { ProcessSendDeviceInfoCallback(deviceInfo, RequestDoneChecker); return true; } DslmUtilsStartOnceTimerTask(notify->keep, TimerProcessSdkRequestTimeout, deviceInfo); return true; } static bool ProcessSendRequestFailed(const StateMachine *machine, uint32_t event, const void *para) { #define ERR_SESSION_OPEN_FAILED 2 DslmDeviceInfo *info = STATE_MACHINE_ENTRY(machine, DslmDeviceInfo, machine); if (para == NULL) { return false; } uint32_t reason = *(uint32_t *)para; info->result = reason; if (reason == ERR_SESSION_OPEN_FAILED) { info->result = ERR_MSG_OPEN_SESSION; StopSendDeviceInfoRequestTimer(info); ProcessSendDeviceInfoCallback(info, RequestDoneChecker); return false; } return CheckTimesAndSendCredRequest(info, false); } static bool ProcessDeviceOffline(const StateMachine *machine, uint32_t event, const void *para) { DslmDeviceInfo *info = STATE_MACHINE_ENTRY(machine, DslmDeviceInfo, machine); info->onlineStatus = ONLINE_STATUS_OFFLINE; info->queryTimes = 0; info->lastOfflineTime = GetMillisecondSinceBoot(); StopSendDeviceInfoRequestTimer(info); ProcessSendDeviceInfoCallback(info, RequestDoneChecker); return true; } static bool ProcessVerifyCredMessage(const StateMachine *machine, uint32_t event, const void *para) { DslmDeviceInfo *deviceInfo = STATE_MACHINE_ENTRY(machine, DslmDeviceInfo, machine); MessageBuff *buff = (MessageBuff *)para; deviceInfo->lastResponseTime = GetMillisecondSinceBoot(); deviceInfo->result = (uint32_t)VerifyDeviceInfoResponse(deviceInfo, buff); deviceInfo->lastVerifyTime = GetMillisecondSinceBoot(); DslmFinishProcessTraceAsync("SendCredRequest", machine->machineId, deviceInfo->queryTimes); ProcessSendDeviceInfoCallback(deviceInfo, RequestDoneChecker); if (deviceInfo->result == SUCCESS) { SECURITY_LOG_INFO("ProcessVerifyCredMessage success, level is %{public}u", deviceInfo->credInfo.credLevel); StopSendDeviceInfoRequestTimer(deviceInfo); return true; } (void)CheckTimesAndSendCredRequest(deviceInfo, false); return false; } static bool ProcessSdkTimeout(const StateMachine *machine, uint32_t event, const void *para) { DslmDeviceInfo *info = STATE_MACHINE_ENTRY(machine, DslmDeviceInfo, machine); ProcessSendDeviceInfoCallback(info, SdkTimeoutChecker); return true; } static bool SdkTimeoutChecker(const DslmDeviceInfo *devInfo, const DslmNotifyListNode *node, DslmCallbackInfo *cbInfo, uint32_t *result) { uint64_t curr = GetMillisecondSinceBoot(); if (node->start + node->keep > curr) { return false; } SECURITY_LOG_INFO("SdkTimeout, device is %{public}x, owner is %{public}u, cookie is %{public}u, keep is %{public}u", devInfo->machine.machineId, node->owner, node->cookie, node->keep); *result = ERR_TIMEOUT; cbInfo->level = 0; cbInfo->extraLen = 0; cbInfo->extraBuff = NULL; return true; } static bool RequestDoneChecker(const DslmDeviceInfo *devInfo, const DslmNotifyListNode *node, DslmCallbackInfo *cbInfo, uint32_t *result) { *result = devInfo->result; cbInfo->level = devInfo->credInfo.credLevel; cbInfo->extraLen = 0; cbInfo->extraBuff = NULL; SECURITY_LOG_INFO( "RequestDone, device is %{public}x, owner is %{public}u, cookie is %{public}u, keep is %{public}u", devInfo->machine.machineId, node->owner, node->cookie, node->keep); return true; } static void RefreshNotifyList(DslmDeviceInfo *info) { if (info == NULL) { return; } // just refresh the notify list size ListNode *node = NULL; uint32_t size = 0; FOREACH_LIST_NODE (node, &info->notifyList) { size++; } info->notifyListSize = size; SECURITY_LOG_INFO("device %{public}x 's notify list size update to %{public}u", info->machine.machineId, info->notifyListSize); } static void RefreshHistoryList(DslmDeviceInfo *info) { if (info == NULL) { return; } // only hold the lasted MAX_HISTORY_CNT node ListNode *node = NULL; ListNode *temp = NULL; uint32_t historyCnt = 0; FOREACH_LIST_NODE_SAFE (node, &info->historyList, temp) { historyCnt++; } uint32_t delCnt = historyCnt > MAX_HISTORY_CNT ? (historyCnt - MAX_HISTORY_CNT) : 0; info->historyListSize = historyCnt - delCnt; FOREACH_LIST_NODE_SAFE (node, &info->historyList, temp) { if (delCnt <= 0) { break; } delCnt--; DslmNotifyListNode *notifyNode = LIST_ENTRY(node, DslmNotifyListNode, linkNode); RemoveListNode(node); FREE(notifyNode); } } void InitDslmStateMachine(DslmDeviceInfo *info) { if (info == NULL) { return; } uint32_t machineId = GenerateMachineId(&info->identity); InitStateMachine(&info->machine, machineId, STATE_INIT); SECURITY_LOG_INFO("InitDslmStateMachine success, machineId is %{public}x", machineId); } void ScheduleDslmStateMachine(DslmDeviceInfo *info, uint32_t event, const void *para) { if (info == NULL) { return; } static const StateNode stateNodes[] = { {STATE_INIT, EVENT_DEVICE_ONLINE, ProcessDeviceOnline, STATE_WAITING_CRED_RSP, STATE_FAILED}, {STATE_INIT, EVENT_SDK_GET, ProcessSdkRequest, STATE_INIT, STATE_INIT}, {STATE_WAITING_CRED_RSP, EVENT_DEVICE_ONLINE, ProcessDeviceOnline, STATE_WAITING_CRED_RSP, STATE_FAILED}, {STATE_WAITING_CRED_RSP, EVENT_CRED_RSP, ProcessVerifyCredMessage, STATE_SUCCESS, STATE_FAILED}, {STATE_WAITING_CRED_RSP, EVENT_MSG_SEND_FAILED, ProcessSendRequestFailed, STATE_WAITING_CRED_RSP, STATE_FAILED}, {STATE_WAITING_CRED_RSP, EVENT_TIME_OUT, ProcessSendCredRequest, STATE_WAITING_CRED_RSP, STATE_FAILED}, {STATE_WAITING_CRED_RSP, EVENT_DEVICE_OFFLINE, ProcessDeviceOffline, STATE_INIT, STATE_INIT}, {STATE_WAITING_CRED_RSP, EVENT_TO_SYNC, NULL, STATE_SUCCESS, STATE_SUCCESS}, {STATE_WAITING_CRED_RSP, EVENT_SDK_GET, ProcessSdkRequest, STATE_WAITING_CRED_RSP, STATE_WAITING_CRED_RSP}, {STATE_WAITING_CRED_RSP, EVENT_SDK_TIMEOUT, ProcessSdkTimeout, STATE_WAITING_CRED_RSP, STATE_WAITING_CRED_RSP}, {STATE_SUCCESS, EVENT_DEVICE_OFFLINE, ProcessDeviceOffline, STATE_INIT, STATE_INIT}, {STATE_SUCCESS, EVENT_SDK_GET, ProcessSdkRequest, STATE_SUCCESS, STATE_SUCCESS}, {STATE_FAILED, EVENT_DEVICE_ONLINE, ProcessDeviceOnline, STATE_WAITING_CRED_RSP, STATE_FAILED}, {STATE_FAILED, EVENT_CRED_RSP, ProcessVerifyCredMessage, STATE_SUCCESS, STATE_FAILED}, {STATE_FAILED, EVENT_DEVICE_OFFLINE, ProcessDeviceOffline, STATE_INIT, STATE_INIT}, {STATE_FAILED, EVENT_CHECK, ProcessSendCredRequest, STATE_WAITING_CRED_RSP, STATE_WAITING_CRED_RSP}, {STATE_FAILED, EVENT_SDK_GET, ProcessSdkRequest, STATE_FAILED, STATE_FAILED}, {STATE_FAILED, EVENT_SDK_TIMEOUT, ProcessSdkTimeout, STATE_FAILED, STATE_FAILED}, }; static const uint32_t nodeCnt = sizeof(stateNodes) / sizeof(StateNode); DslmStartStateMachineTrace(info->machine.machineId, event); ScheduleMachine(stateNodes, nodeCnt, &info->machine, event, para); DslmFinishProcessTrace(); } uint32_t GetCurrentMachineState(const DslmDeviceInfo *info) { if (info == NULL) { return STATE_FAILED; } return info->machine.currState; } void LockDslmStateMachine(DslmDeviceInfo *info) { LockMutex(&info->machine.mutex); } void UnLockDslmStateMachine(DslmDeviceInfo *info) { UnlockMutex(&info->machine.mutex); }