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(¬ify->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 }