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