1/*
2 * Copyright (c) 2022 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16#include "dslm_fsm_process.h"
17
18#include <securec.h>
19#include <stdbool.h>
20#include <stddef.h>
21
22#include "device_security_defines.h"
23#include "utils_datetime.h"
24#include "utils_dslm_list.h"
25#include "utils_hexstring.h"
26#include "utils_log.h"
27#include "utils_mem.h"
28#include "utils_mutex.h"
29#include "utils_state_machine.h"
30#include "utils_timer.h"
31
32#include "dslm_callback_info.h"
33#include "dslm_core_defines.h"
34#include "dslm_cred.h"
35#include "dslm_device_list.h"
36#include "dslm_hitrace.h"
37#include "dslm_inner_process.h"
38#include "dslm_msg_serialize.h"
39#include "dslm_notify_node.h"
40
41#define REQUEST_INTERVAL (24 * 60 * 60 * 1000)
42#define DEFAULT_TYPE 10
43#define TYPE_PLACE 8
44
45typedef bool DslmInfoChecker(const DslmDeviceInfo *devInfo, const DslmNotifyListNode *node, DslmCallbackInfo *cbInfo,
46    uint32_t *result);
47
48static bool SdkTimeoutChecker(const DslmDeviceInfo *devInfo, const DslmNotifyListNode *node, DslmCallbackInfo *cbInfo,
49    uint32_t *result);
50static bool RequestDoneChecker(const DslmDeviceInfo *devInfo, const DslmNotifyListNode *node, DslmCallbackInfo *cbInfo,
51    uint32_t *result);
52
53static uint32_t GenerateMachineId(const DeviceIdentify *identity);
54static bool CheckTimesAndSendCredRequest(DslmDeviceInfo *info, bool enforce);
55static void StopSendDeviceInfoRequestTimer(DslmDeviceInfo *info);
56static void ProcessSendDeviceInfoCallback(DslmDeviceInfo *info, DslmInfoChecker checker);
57
58static void TimerProcessSendDeviceInfoRequestTimeOut(const void *context);
59static void TimerProcessSdkRequestTimeout(const void *context);
60
61static bool ProcessDeviceOnline(const StateMachine *machine, uint32_t event, const void *para);
62static bool ProcessSendCredRequest(const StateMachine *machine, uint32_t event, const void *para);
63static bool ProcessSdkRequest(const StateMachine *machine, uint32_t event, const void *para);
64static bool ProcessSendRequestFailed(const StateMachine *machine, uint32_t event, const void *para);
65static bool ProcessDeviceOffline(const StateMachine *machine, uint32_t event, const void *para);
66static bool ProcessVerifyCredMessage(const StateMachine *machine, uint32_t event, const void *para);
67static bool ProcessSdkTimeout(const StateMachine *machine, uint32_t event, const void *para);
68
69static void RefreshNotifyList(DslmDeviceInfo *info);
70static void RefreshHistoryList(DslmDeviceInfo *info);
71
72static uint32_t GenerateMachineId(const DeviceIdentify *identity)
73{
74#define MASK_LOW 0x00ffU
75#define MACHINE_ID_LENGTH 4U
76#define SHIFT_LENGTH 8U
77#define MASK_HIGH 0xff00U
78    uint16_t machineId = 0;
79    DslmHexStringToByte((const char *)identity->identity, MACHINE_ID_LENGTH, (uint8_t *)&machineId, sizeof(machineId));
80    return ((machineId & MASK_HIGH) >> SHIFT_LENGTH) | ((machineId & MASK_LOW) << SHIFT_LENGTH);
81}
82
83static void TimerProcessSendDeviceInfoRequestTimeOut(const void *context)
84{
85    if (context == NULL) {
86        return;
87    }
88    // the context info will never be freed, so feel free use it.
89    ScheduleDslmStateMachine((DslmDeviceInfo *)context, EVENT_TIME_OUT, NULL);
90}
91
92static void TimerProcessSdkRequestTimeout(const void *context)
93{
94    if (context == NULL) {
95        return;
96    }
97    // the context info will never be freed, so feel free use it.
98    ScheduleDslmStateMachine((DslmDeviceInfo *)context, EVENT_SDK_TIMEOUT, NULL);
99}
100
101static void StopSendDeviceInfoRequestTimer(DslmDeviceInfo *info)
102{
103    if (info->timeHandle != 0) {
104        DslmUtilsStopTimerTask(info->timeHandle);
105        info->timeHandle = 0;
106    }
107}
108
109static void StartSendDeviceInfoRequestTimer(DslmDeviceInfo *info)
110{
111    info->timeHandle =
112        DslmUtilsStartOnceTimerTask(SEND_MSG_TIMEOUT_LEN, TimerProcessSendDeviceInfoRequestTimeOut, info);
113}
114
115static bool CheckTimesAndSendCredRequest(DslmDeviceInfo *info, bool enforce)
116{
117#ifndef MAX_SEND_TIMES
118#define MAX_SEND_TIMES 5
119#endif
120
121#ifndef SEND_MSG_TIMEOUT_LEN
122#define SEND_MSG_TIMEOUT_LEN 40000
123#endif
124
125    if (!enforce && info->queryTimes > MAX_SEND_TIMES) {
126        return false;
127    }
128    DslmStartProcessTraceAsync("SendCredRequest", info->machine.machineId, info->queryTimes + 1);
129    CheckAndGenerateChallenge(info);
130    SendDeviceInfoRequest(info);
131    info->queryTimes++;
132    info->lastRequestTime = GetMillisecondSinceBoot();
133
134    StopSendDeviceInfoRequestTimer(info);
135    StartSendDeviceInfoRequestTimer(info);
136    return true;
137}
138
139static void ProcessSendDeviceInfoCallback(DslmDeviceInfo *info, DslmInfoChecker checker)
140{
141#ifndef MAX_HISTORY_CNT
142#define MAX_HISTORY_CNT 30U
143#endif
144
145    if (info == NULL || checker == NULL) {
146        return;
147    }
148    ListNode *node = NULL;
149    ListNode *temp = NULL;
150    SECURITY_LOG_DEBUG("ProcessSendDeviceInfoCallback for device %{public}x.", info->machine.machineId);
151    FOREACH_LIST_NODE_SAFE (node, &info->notifyList, temp) {
152        DslmNotifyListNode *notifyNode = LIST_ENTRY(node, DslmNotifyListNode, linkNode);
153        uint32_t result;
154        DslmCallbackInfo cbInfo;
155        bool check = checker(info, notifyNode, &cbInfo, &result);
156        if (!check) {
157            continue;
158        }
159        SECURITY_LOG_DEBUG("ProcessSendDeviceInfoCallback result %{public}u for device %{public}x, level %{public}u.",
160            result, info->machine.machineId, cbInfo.level);
161
162        notifyNode->requestCallback(notifyNode->owner, notifyNode->cookie, result, &cbInfo);
163        notifyNode->stop = GetMillisecondSinceBoot();
164        notifyNode->result = result;
165
166        RemoveListNode(node);
167        DslmFinishProcessTraceAsync("SDK_GET", notifyNode->owner, notifyNode->cookie);
168
169        AddListNodeBefore(node, &info->historyList);
170    }
171
172    RefreshNotifyList(info);
173    RefreshHistoryList(info);
174}
175
176static bool CheckNeedToResend(const DslmDeviceInfo *info)
177{
178    if (info->credInfo.credLevel > 0) {
179        return false;
180    }
181    if (info->credInfo.credLevel == 0) {
182        return true;
183    }
184    if (info->lastOnlineTime < info->lastRequestTime) {
185        return true;
186    }
187    if (info->lastOnlineTime - info->lastRequestTime > (uint64_t)REQUEST_INTERVAL) {
188        return true;
189    }
190    return false;
191}
192
193static bool ProcessDeviceOnline(const StateMachine *machine, uint32_t event, const void *para)
194{
195    DslmDeviceInfo *info = STATE_MACHINE_ENTRY(machine, DslmDeviceInfo, machine);
196    uint32_t deviceAttributes = 0;
197    if (para != NULL) {
198        deviceAttributes = *(uint32_t *)para;
199    }
200    uint32_t level = deviceAttributes & 0xFF;
201    uint32_t osType = (deviceAttributes & 0xFF00) >> TYPE_PLACE;
202    info->osType = osType;
203    if (level == 0 && osType == DEFAULT_TYPE) {
204        level = 1;
205        SECURITY_LOG_INFO("level set 1");
206    }
207    if (level > 0) {
208        info->credInfo.credLevel = level;
209        info->result = SUCCESS;
210    }
211    info->onlineStatus = ONLINE_STATUS_ONLINE;
212    info->queryTimes = 0;
213    info->lastOnlineTime = GetMillisecondSinceBoot();
214    if (!CheckNeedToResend(info)) {
215        SECURITY_LOG_INFO("last request time is last than 24 hours");
216        ScheduleDslmStateMachine(info, EVENT_TO_SYNC, NULL);
217        return true;
218    }
219    return ProcessSendCredRequest(machine, event, para);
220}
221
222static bool ProcessSendCredRequest(const StateMachine *machine, uint32_t event, const void *para)
223{
224    DslmDeviceInfo *info = STATE_MACHINE_ENTRY(machine, DslmDeviceInfo, machine);
225    bool enforce = (para != NULL);
226    return CheckTimesAndSendCredRequest(info, enforce);
227}
228
229static bool ProcessSdkRequest(const StateMachine *machine, uint32_t event, const void *para)
230{
231    DslmDeviceInfo *deviceInfo = STATE_MACHINE_ENTRY(machine, DslmDeviceInfo, machine);
232    DslmNotifyListNode *inputNotify = (DslmNotifyListNode *)para;
233    if (inputNotify == NULL) {
234        return false;
235    }
236
237    DslmNotifyListNode *notify = MALLOC(sizeof(DslmNotifyListNode));
238    if (notify == NULL) {
239        SECURITY_LOG_ERROR("malloc failed, notifyNode is null");
240        return false;
241    }
242    (void)memset_s(notify, sizeof(DslmNotifyListNode), 0, sizeof(DslmNotifyListNode));
243    notify->owner = inputNotify->owner;
244    notify->cookie = inputNotify->cookie;
245    notify->requestCallback = inputNotify->requestCallback;
246    notify->start = inputNotify->start;
247    notify->keep = inputNotify->keep;
248    if (notify->cookie == 0 || notify->requestCallback == NULL) {
249        SECURITY_LOG_ERROR("ProcessSdkRequest invalid cookie or callback.");
250        FREE(notify);
251        notify = NULL;
252        return false;
253    }
254
255    DslmStartProcessTraceAsync("SDK_GET", notify->owner, notify->cookie);
256    AddListNode(&notify->linkNode, &deviceInfo->notifyList);
257    RefreshNotifyList(deviceInfo);
258    SECURITY_LOG_DEBUG(
259        "ProcessSdkRequest, device is %{public}x, owner is %{public}u, cookie is %{public}u, keep is %{public}u",
260        deviceInfo->machine.machineId, notify->owner, notify->cookie, notify->keep);
261    uint32_t state = GetCurrentMachineState(deviceInfo);
262    if (state == STATE_SUCCESS || state == STATE_FAILED || deviceInfo->credInfo.credLevel != 0) {
263        ProcessSendDeviceInfoCallback(deviceInfo, RequestDoneChecker);
264        return true;
265    }
266
267    DslmUtilsStartOnceTimerTask(notify->keep, TimerProcessSdkRequestTimeout, deviceInfo);
268    return true;
269}
270
271static bool ProcessSendRequestFailed(const StateMachine *machine, uint32_t event, const void *para)
272{
273#define ERR_SESSION_OPEN_FAILED 2
274    DslmDeviceInfo *info = STATE_MACHINE_ENTRY(machine, DslmDeviceInfo, machine);
275    if (para == NULL) {
276        return false;
277    }
278
279    uint32_t reason = *(uint32_t *)para;
280    info->result = reason;
281    if (reason == ERR_SESSION_OPEN_FAILED) {
282        info->result = ERR_MSG_OPEN_SESSION;
283        StopSendDeviceInfoRequestTimer(info);
284        ProcessSendDeviceInfoCallback(info, RequestDoneChecker);
285        return false;
286    }
287
288    return CheckTimesAndSendCredRequest(info, false);
289}
290
291static bool ProcessDeviceOffline(const StateMachine *machine, uint32_t event, const void *para)
292{
293    DslmDeviceInfo *info = STATE_MACHINE_ENTRY(machine, DslmDeviceInfo, machine);
294    info->onlineStatus = ONLINE_STATUS_OFFLINE;
295    info->queryTimes = 0;
296    info->lastOfflineTime = GetMillisecondSinceBoot();
297    StopSendDeviceInfoRequestTimer(info);
298    ProcessSendDeviceInfoCallback(info, RequestDoneChecker);
299    return true;
300}
301
302static bool ProcessVerifyCredMessage(const StateMachine *machine, uint32_t event, const void *para)
303{
304    DslmDeviceInfo *deviceInfo = STATE_MACHINE_ENTRY(machine, DslmDeviceInfo, machine);
305    MessageBuff *buff = (MessageBuff *)para;
306
307    deviceInfo->lastResponseTime = GetMillisecondSinceBoot();
308    deviceInfo->result = (uint32_t)VerifyDeviceInfoResponse(deviceInfo, buff);
309    deviceInfo->lastVerifyTime = GetMillisecondSinceBoot();
310    DslmFinishProcessTraceAsync("SendCredRequest", machine->machineId, deviceInfo->queryTimes);
311    ProcessSendDeviceInfoCallback(deviceInfo, RequestDoneChecker);
312
313    if (deviceInfo->result == SUCCESS) {
314        SECURITY_LOG_INFO("ProcessVerifyCredMessage success, level is %{public}u", deviceInfo->credInfo.credLevel);
315        StopSendDeviceInfoRequestTimer(deviceInfo);
316        return true;
317    }
318
319    (void)CheckTimesAndSendCredRequest(deviceInfo, false);
320    return false;
321}
322
323static bool ProcessSdkTimeout(const StateMachine *machine, uint32_t event, const void *para)
324{
325    DslmDeviceInfo *info = STATE_MACHINE_ENTRY(machine, DslmDeviceInfo, machine);
326    ProcessSendDeviceInfoCallback(info, SdkTimeoutChecker);
327    return true;
328}
329
330static bool SdkTimeoutChecker(const DslmDeviceInfo *devInfo, const DslmNotifyListNode *node, DslmCallbackInfo *cbInfo,
331    uint32_t *result)
332{
333    uint64_t curr = GetMillisecondSinceBoot();
334    if (node->start + node->keep > curr) {
335        return false;
336    }
337
338    SECURITY_LOG_INFO("SdkTimeout, device is %{public}x, owner is %{public}u, cookie is %{public}u, keep is %{public}u",
339        devInfo->machine.machineId, node->owner, node->cookie, node->keep);
340
341    *result = ERR_TIMEOUT;
342    cbInfo->level = 0;
343    cbInfo->extraLen = 0;
344    cbInfo->extraBuff = NULL;
345    return true;
346}
347
348static bool RequestDoneChecker(const DslmDeviceInfo *devInfo, const DslmNotifyListNode *node, DslmCallbackInfo *cbInfo,
349    uint32_t *result)
350{
351    *result = devInfo->result;
352    cbInfo->level = devInfo->credInfo.credLevel;
353    cbInfo->extraLen = 0;
354    cbInfo->extraBuff = NULL;
355
356    SECURITY_LOG_INFO(
357        "RequestDone, device is %{public}x, owner is %{public}u, cookie is %{public}u, keep is %{public}u",
358        devInfo->machine.machineId, node->owner, node->cookie, node->keep);
359
360    return true;
361}
362
363static void RefreshNotifyList(DslmDeviceInfo *info)
364{
365    if (info == NULL) {
366        return;
367    }
368
369    // just refresh the notify list size
370    ListNode *node = NULL;
371    uint32_t size = 0;
372    FOREACH_LIST_NODE (node, &info->notifyList) {
373        size++;
374    }
375    info->notifyListSize = size;
376
377    SECURITY_LOG_INFO("device %{public}x 's notify list size update to %{public}u", info->machine.machineId,
378        info->notifyListSize);
379}
380
381static void RefreshHistoryList(DslmDeviceInfo *info)
382{
383    if (info == NULL) {
384        return;
385    }
386
387    // only hold the lasted MAX_HISTORY_CNT node
388    ListNode *node = NULL;
389    ListNode *temp = NULL;
390
391    uint32_t historyCnt = 0;
392    FOREACH_LIST_NODE_SAFE (node, &info->historyList, temp) {
393        historyCnt++;
394    }
395    uint32_t delCnt = historyCnt > MAX_HISTORY_CNT ? (historyCnt - MAX_HISTORY_CNT) : 0;
396
397    info->historyListSize = historyCnt - delCnt;
398
399    FOREACH_LIST_NODE_SAFE (node, &info->historyList, temp) {
400        if (delCnt <= 0) {
401            break;
402        }
403        delCnt--;
404        DslmNotifyListNode *notifyNode = LIST_ENTRY(node, DslmNotifyListNode, linkNode);
405        RemoveListNode(node);
406        FREE(notifyNode);
407    }
408}
409
410void InitDslmStateMachine(DslmDeviceInfo *info)
411{
412    if (info == NULL) {
413        return;
414    }
415    uint32_t machineId = GenerateMachineId(&info->identity);
416    InitStateMachine(&info->machine, machineId, STATE_INIT);
417    SECURITY_LOG_INFO("InitDslmStateMachine success, machineId is %{public}x", machineId);
418}
419
420void ScheduleDslmStateMachine(DslmDeviceInfo *info, uint32_t event, const void *para)
421{
422    if (info == NULL) {
423        return;
424    }
425
426    static const StateNode stateNodes[] = {
427        {STATE_INIT, EVENT_DEVICE_ONLINE, ProcessDeviceOnline, STATE_WAITING_CRED_RSP, STATE_FAILED},
428        {STATE_INIT, EVENT_SDK_GET, ProcessSdkRequest, STATE_INIT, STATE_INIT},
429        {STATE_WAITING_CRED_RSP, EVENT_DEVICE_ONLINE, ProcessDeviceOnline, STATE_WAITING_CRED_RSP, STATE_FAILED},
430        {STATE_WAITING_CRED_RSP, EVENT_CRED_RSP, ProcessVerifyCredMessage, STATE_SUCCESS, STATE_FAILED},
431        {STATE_WAITING_CRED_RSP, EVENT_MSG_SEND_FAILED, ProcessSendRequestFailed, STATE_WAITING_CRED_RSP, STATE_FAILED},
432        {STATE_WAITING_CRED_RSP, EVENT_TIME_OUT, ProcessSendCredRequest, STATE_WAITING_CRED_RSP, STATE_FAILED},
433        {STATE_WAITING_CRED_RSP, EVENT_DEVICE_OFFLINE, ProcessDeviceOffline, STATE_INIT, STATE_INIT},
434        {STATE_WAITING_CRED_RSP, EVENT_TO_SYNC, NULL, STATE_SUCCESS, STATE_SUCCESS},
435        {STATE_WAITING_CRED_RSP, EVENT_SDK_GET, ProcessSdkRequest, STATE_WAITING_CRED_RSP, STATE_WAITING_CRED_RSP},
436        {STATE_WAITING_CRED_RSP, EVENT_SDK_TIMEOUT, ProcessSdkTimeout, STATE_WAITING_CRED_RSP, STATE_WAITING_CRED_RSP},
437        {STATE_SUCCESS, EVENT_DEVICE_OFFLINE, ProcessDeviceOffline, STATE_INIT, STATE_INIT},
438        {STATE_SUCCESS, EVENT_SDK_GET, ProcessSdkRequest, STATE_SUCCESS, STATE_SUCCESS},
439        {STATE_FAILED, EVENT_DEVICE_ONLINE, ProcessDeviceOnline, STATE_WAITING_CRED_RSP, STATE_FAILED},
440        {STATE_FAILED, EVENT_CRED_RSP, ProcessVerifyCredMessage, STATE_SUCCESS, STATE_FAILED},
441        {STATE_FAILED, EVENT_DEVICE_OFFLINE, ProcessDeviceOffline, STATE_INIT, STATE_INIT},
442        {STATE_FAILED, EVENT_CHECK, ProcessSendCredRequest, STATE_WAITING_CRED_RSP, STATE_WAITING_CRED_RSP},
443        {STATE_FAILED, EVENT_SDK_GET, ProcessSdkRequest, STATE_FAILED, STATE_FAILED},
444        {STATE_FAILED, EVENT_SDK_TIMEOUT, ProcessSdkTimeout, STATE_FAILED, STATE_FAILED},
445    };
446
447    static const uint32_t nodeCnt = sizeof(stateNodes) / sizeof(StateNode);
448    DslmStartStateMachineTrace(info->machine.machineId, event);
449    ScheduleMachine(stateNodes, nodeCnt, &info->machine, event, para);
450    DslmFinishProcessTrace();
451}
452
453uint32_t GetCurrentMachineState(const DslmDeviceInfo *info)
454{
455    if (info == NULL) {
456        return STATE_FAILED;
457    }
458    return info->machine.currState;
459}
460
461void LockDslmStateMachine(DslmDeviceInfo *info)
462{
463    LockMutex(&info->machine.mutex);
464}
465
466void UnLockDslmStateMachine(DslmDeviceInfo *info)
467{
468    UnlockMutex(&info->machine.mutex);
469}