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 
45 typedef bool DslmInfoChecker(const DslmDeviceInfo *devInfo, const DslmNotifyListNode *node, DslmCallbackInfo *cbInfo,
46     uint32_t *result);
47 
48 static bool SdkTimeoutChecker(const DslmDeviceInfo *devInfo, const DslmNotifyListNode *node, DslmCallbackInfo *cbInfo,
49     uint32_t *result);
50 static bool RequestDoneChecker(const DslmDeviceInfo *devInfo, const DslmNotifyListNode *node, DslmCallbackInfo *cbInfo,
51     uint32_t *result);
52 
53 static uint32_t GenerateMachineId(const DeviceIdentify *identity);
54 static bool CheckTimesAndSendCredRequest(DslmDeviceInfo *info, bool enforce);
55 static void StopSendDeviceInfoRequestTimer(DslmDeviceInfo *info);
56 static void ProcessSendDeviceInfoCallback(DslmDeviceInfo *info, DslmInfoChecker checker);
57 
58 static void TimerProcessSendDeviceInfoRequestTimeOut(const void *context);
59 static void TimerProcessSdkRequestTimeout(const void *context);
60 
61 static bool ProcessDeviceOnline(const StateMachine *machine, uint32_t event, const void *para);
62 static bool ProcessSendCredRequest(const StateMachine *machine, uint32_t event, const void *para);
63 static bool ProcessSdkRequest(const StateMachine *machine, uint32_t event, const void *para);
64 static bool ProcessSendRequestFailed(const StateMachine *machine, uint32_t event, const void *para);
65 static bool ProcessDeviceOffline(const StateMachine *machine, uint32_t event, const void *para);
66 static bool ProcessVerifyCredMessage(const StateMachine *machine, uint32_t event, const void *para);
67 static bool ProcessSdkTimeout(const StateMachine *machine, uint32_t event, const void *para);
68 
69 static void RefreshNotifyList(DslmDeviceInfo *info);
70 static void RefreshHistoryList(DslmDeviceInfo *info);
71 
GenerateMachineId(const DeviceIdentify * identity)72 static 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 
TimerProcessSendDeviceInfoRequestTimeOut(const void * context)83 static 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 
TimerProcessSdkRequestTimeout(const void * context)92 static 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 
StopSendDeviceInfoRequestTimer(DslmDeviceInfo * info)101 static void StopSendDeviceInfoRequestTimer(DslmDeviceInfo *info)
102 {
103     if (info->timeHandle != 0) {
104         DslmUtilsStopTimerTask(info->timeHandle);
105         info->timeHandle = 0;
106     }
107 }
108 
StartSendDeviceInfoRequestTimer(DslmDeviceInfo * info)109 static void StartSendDeviceInfoRequestTimer(DslmDeviceInfo *info)
110 {
111     info->timeHandle =
112         DslmUtilsStartOnceTimerTask(SEND_MSG_TIMEOUT_LEN, TimerProcessSendDeviceInfoRequestTimeOut, info);
113 }
114 
CheckTimesAndSendCredRequest(DslmDeviceInfo * info,bool enforce)115 static 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 
ProcessSendDeviceInfoCallback(DslmDeviceInfo * info,DslmInfoChecker checker)139 static 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 
CheckNeedToResend(const DslmDeviceInfo * info)176 static 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 
ProcessDeviceOnline(const StateMachine * machine,uint32_t event,const void * para)193 static 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 
ProcessSendCredRequest(const StateMachine * machine,uint32_t event,const void * para)222 static 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 
ProcessSdkRequest(const StateMachine * machine,uint32_t event,const void * para)229 static 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 
ProcessSendRequestFailed(const StateMachine * machine,uint32_t event,const void * para)271 static 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 
ProcessDeviceOffline(const StateMachine * machine,uint32_t event,const void * para)291 static 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 
ProcessVerifyCredMessage(const StateMachine * machine,uint32_t event,const void * para)302 static 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 
ProcessSdkTimeout(const StateMachine * machine,uint32_t event,const void * para)323 static 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 
SdkTimeoutChecker(const DslmDeviceInfo * devInfo,const DslmNotifyListNode * node,DslmCallbackInfo * cbInfo,uint32_t * result)330 static 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 
RequestDoneChecker(const DslmDeviceInfo * devInfo,const DslmNotifyListNode * node,DslmCallbackInfo * cbInfo,uint32_t * result)348 static 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 
RefreshNotifyList(DslmDeviceInfo * info)363 static 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 
RefreshHistoryList(DslmDeviceInfo * info)381 static 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 
InitDslmStateMachine(DslmDeviceInfo * info)410 void 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 
ScheduleDslmStateMachine(DslmDeviceInfo * info,uint32_t event,const void * para)420 void 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 
GetCurrentMachineState(const DslmDeviceInfo * info)453 uint32_t GetCurrentMachineState(const DslmDeviceInfo *info)
454 {
455     if (info == NULL) {
456         return STATE_FAILED;
457     }
458     return info->machine.currState;
459 }
460 
LockDslmStateMachine(DslmDeviceInfo * info)461 void LockDslmStateMachine(DslmDeviceInfo *info)
462 {
463     LockMutex(&info->machine.mutex);
464 }
465 
UnLockDslmStateMachine(DslmDeviceInfo * info)466 void UnLockDslmStateMachine(DslmDeviceInfo *info)
467 {
468     UnlockMutex(&info->machine.mutex);
469 }