1 /*
2  * Copyright (c) 2022-2023 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 "hks_session_manager.h"
17 #include "hks_client_service_util.h"
18 
19 #include <inttypes.h>
20 #include <pthread.h>
21 #include <sched.h>
22 #include <securec.h>
23 #include <stdio.h>
24 
25 #include "hks_log.h"
26 #include "hks_mem.h"
27 #include "hks_param.h"
28 #include "hks_template.h"
29 #include "huks_access.h"
30 #include "securec.h"
31 #include "hks_util.h"
32 
33 #define MAX_OPERATIONS_COUNT 96
34 
35 #ifdef HKS_SUPPORT_ACCESS_TOKEN
36 #define MAX_OPERATIONS_EACH_TOKEN_ID 32
37 #else
38 #define MAX_OPERATIONS_EACH_TOKEN_ID MAX_OPERATIONS_COUNT
39 #endif
40 
41 #define S_TO_MS 1000
42 
43 static struct DoubleList g_operationList = { &g_operationList, &g_operationList };
44 static uint32_t g_operationCount = 0;
45 static pthread_mutex_t g_lock = PTHREAD_MUTEX_INITIALIZER;
46 
DeleteKeyNode(uint64_t operationHandle)47 static void DeleteKeyNode(uint64_t operationHandle)
48 {
49     uint8_t *handle = (uint8_t *)HksMalloc(sizeof(uint64_t));
50     if (handle == NULL) {
51         HKS_LOG_E("malloc failed");
52         return;
53     }
54     (void)memcpy_s(handle, sizeof(uint64_t), &operationHandle, sizeof(uint64_t));
55     struct HksBlob handleBlob = { sizeof(uint64_t), handle };
56 
57     struct HksParamSet *paramSet = NULL;
58     if (HksInitParamSet(&paramSet) != HKS_SUCCESS) {
59         HKS_FREE(handle);
60         return;
61     }
62 
63     (void)HuksAccessAbort(&handleBlob, paramSet);
64 
65     HksFreeParamSet(&paramSet);
66     HKS_FREE(handle);
67 }
68 
69 /* Need to lock before calling FreeOperation */
FreeOperation(struct HksOperation ** operation)70 static void FreeOperation(struct HksOperation **operation)
71 {
72     if (operation == NULL || *operation == NULL) {
73         return;
74     }
75     RemoveDoubleListNode(&(*operation)->listHead);
76     HKS_FREE_BLOB((*operation)->processInfo.userId);
77     HKS_FREE_BLOB((*operation)->processInfo.processName);
78     HKS_FREE(*operation);
79 }
80 
81 /* Need to lock before calling DeleteKeyNodeAndDecreaseGlobalCount */
DeleteKeyNodeAndDecreaseGlobalCount(struct HksOperation * operation)82 static void DeleteKeyNodeAndDecreaseGlobalCount(struct HksOperation *operation)
83 {
84     DeleteKeyNode(operation->handle);
85     FreeOperation(&operation);
86     --g_operationCount;
87     HKS_LOG_I("delete operation count:%" LOG_PUBLIC "u", g_operationCount);
88 }
89 
90 /* Need to lock before calling DeleteFirstAbortableOperation */
DeleteFirstAbortableOperation(void)91 static bool DeleteFirstAbortableOperation(void)
92 {
93     struct HksOperation *operation = NULL;
94 
95     HKS_DLIST_ITER(operation, &g_operationList) {
96         if (operation == NULL) {
97             continue;
98         }
99         if (operation->isInUse) {
100             HKS_LOG_W("DeleteFirstAbortableOperation can not delete using session! userIdInt %" LOG_PUBLIC "d",
101                 operation->processInfo.userIdInt);
102             continue;
103         }
104         HKS_LOG_E("DeleteFirstAbortableOperation delete old not using session! userIdInt %"
105             LOG_PUBLIC "d", operation->processInfo.userIdInt);
106         DeleteKeyNodeAndDecreaseGlobalCount(operation);
107         return true;
108     }
109     return false;
110 }
111 
112 /* Need to lock before calling DeleteFirstTimeOutBatchOperation */
DeleteFirstTimeOutBatchOperation(void)113 static void DeleteFirstTimeOutBatchOperation(void)
114 {
115     if (g_operationCount < MAX_OPERATIONS_COUNT) {
116         return;
117     }
118     HKS_LOG_I("maximum number of sessions reached: delete timeout session.");
119     struct HksOperation *operation = NULL;
120 
121     HKS_DLIST_ITER(operation, &g_operationList) {
122         if (operation == NULL || !operation->isBatchOperation) {
123             continue;
124         }
125         uint64_t curTime = 0;
126         int32_t ret = HksElapsedRealTime(&curTime);
127         if (ret != HKS_SUCCESS) {
128             HKS_LOG_E("HksElapsedRealTime failed %" LOG_PUBLIC "d, err %" LOG_PUBLIC "s", ret, strerror(errno));
129             continue; // find next and try again
130         }
131         if (operation->batchOperationTimestamp >= curTime) {
132             continue;
133         }
134         if (operation->isInUse) {
135             HKS_LOG_W("Batch operation timeout but is in use, not delete, userIdInt %" LOG_PUBLIC "d",
136                 operation->processInfo.userIdInt);
137             continue;
138         }
139         HKS_LOG_E("Batch operation timeout! delete operation! userIdInt %" LOG_PUBLIC "d",
140             operation->processInfo.userIdInt);
141         // IAR iccarm can not compile `return DeleteKeyNodeAndDecreaseGlobalCount(operation)`
142         // IAR iccarm will report `a void function may not return a value`
143         DeleteKeyNodeAndDecreaseGlobalCount(operation);
144         return;
145     }
146 }
147 
148 /* Need to lock before calling DeleteFirstAbortableOperationForTokenId */
DeleteFirstAbortableOperationForTokenId(uint32_t tokenId)149 static bool DeleteFirstAbortableOperationForTokenId(uint32_t tokenId)
150 {
151     struct HksOperation *operation = NULL;
152     HKS_DLIST_ITER(operation, &g_operationList) {
153         if (operation == NULL || operation->accessTokenId != tokenId) {
154             continue;
155         }
156         if (operation->isInUse) {
157             HKS_LOG_W("DeleteFirstAbortableOperationForTokenId can not delete using session! userIdInt %"
158                 LOG_PUBLIC "d", operation->processInfo.userIdInt);
159             continue;
160         }
161         HKS_LOG_E("DeleteFirstAbortableOperationForTokenId delete old not using session! userIdInt %"
162             LOG_PUBLIC "d", operation->processInfo.userIdInt);
163         DeleteKeyNodeAndDecreaseGlobalCount(operation);
164         return true;
165     }
166     return false;
167 }
168 
169 /* Need to lock before calling DeleteForTokenIdIfExceedLimit */
DeleteForTokenIdIfExceedLimit(uint32_t tokenId)170 static int32_t DeleteForTokenIdIfExceedLimit(uint32_t tokenId)
171 {
172     if (g_operationCount < MAX_OPERATIONS_EACH_TOKEN_ID) {
173         return HKS_SUCCESS;
174     }
175     uint32_t ownedSessionCount = 0;
176     struct HksOperation *operation = NULL;
177     HKS_DLIST_ITER(operation, &g_operationList) {
178         if (operation != NULL && operation->accessTokenId == tokenId) {
179             ++ownedSessionCount;
180         }
181     }
182     if (ownedSessionCount >= MAX_OPERATIONS_EACH_TOKEN_ID) {
183         HKS_LOG_E("current tokenId have owned too many %" LOG_PUBLIC "u sessions", ownedSessionCount);
184         if (DeleteFirstAbortableOperationForTokenId(tokenId)) {
185             return HKS_SUCCESS;
186         }
187         return HKS_ERROR_SESSION_REACHED_LIMIT;
188     }
189     return HKS_SUCCESS;
190 }
191 
AddOperation(struct HksOperation * operation)192 static int32_t AddOperation(struct HksOperation *operation)
193 {
194     pthread_mutex_lock(&g_lock);
195 
196     int32_t ret = HKS_ERROR_SESSION_REACHED_LIMIT;
197     do {
198         DeleteFirstTimeOutBatchOperation();
199 
200         ret = DeleteForTokenIdIfExceedLimit(operation->accessTokenId);
201         HKS_IF_NOT_SUCC_LOGE_BREAK(ret, "DeleteForTokenIdIfExceedLimit fail %" LOG_PUBLIC "d", ret)
202 
203         if (g_operationCount >= MAX_OPERATIONS_COUNT) {
204             HKS_LOG_I("maximum number of sessions reached: delete oldest session.");
205             if (!DeleteFirstAbortableOperation()) {
206                 HKS_LOG_E("DeleteFirstAbortableOperation fail!");
207                 ret = HKS_ERROR_SESSION_REACHED_LIMIT;
208                 break;
209             }
210         }
211 
212         AddNodeAtDoubleListTail(&g_operationList, &operation->listHead);
213         ++g_operationCount;
214         HKS_LOG_I("add operation count:%" LOG_PUBLIC "u", g_operationCount);
215     } while (false);
216     pthread_mutex_unlock(&g_lock);
217     return ret;
218 }
219 
ConstructOperationProcessInfo(const struct HksProcessInfo * processInfo,struct HksOperation * operation)220 static int32_t ConstructOperationProcessInfo(const struct HksProcessInfo *processInfo, struct HksOperation *operation)
221 {
222     /* userIdLen and processNameLen have been checked by calling function */
223     uint32_t userIdLen = processInfo->userId.size;
224     uint32_t processNameLen = processInfo->processName.size;
225 
226     uint8_t *userId = (uint8_t *)HksMalloc(userIdLen);
227     HKS_IF_NULL_LOGE_RETURN(userId, HKS_ERROR_MALLOC_FAIL, "malloc operation userId failed")
228 
229     uint8_t *processName = (uint8_t *)HksMalloc(processNameLen);
230     if (processName == NULL) {
231         HKS_LOG_E("malloc operation process name failed");
232         HKS_FREE(userId);
233         return HKS_ERROR_MALLOC_FAIL;
234     }
235 
236     (void)memcpy_s(userId, userIdLen, processInfo->userId.data, userIdLen);
237     (void)memcpy_s(processName, processNameLen, processInfo->processName.data, processNameLen);
238 
239     operation->processInfo.userId.size = userIdLen;
240     operation->processInfo.userId.data = userId;
241     operation->processInfo.processName.size = processNameLen;
242     operation->processInfo.processName.data = processName;
243     operation->accessTokenId = processInfo->accessTokenId;
244     return HKS_SUCCESS;
245 }
246 
ConstructOperationHandle(const struct HksBlob * operationHandle,uint64_t * handle)247 static int32_t ConstructOperationHandle(const struct HksBlob *operationHandle, uint64_t *handle)
248 {
249     if (operationHandle->size < sizeof(*handle)) {
250         HKS_LOG_E("invalid handle size");
251         return HKS_ERROR_INVALID_ARGUMENT;
252     }
253     if (memcpy_s(handle, sizeof(*handle), operationHandle->data, operationHandle->size) != EOK) {
254         HKS_LOG_E("copy handle failed");
255         return HKS_ERROR_INSUFFICIENT_MEMORY;
256     }
257 
258     return HKS_SUCCESS;
259 }
260 
HksAddBatchTimeToOperation(const struct HksParamSet * paramSet,struct HksOperation * operation)261 static int32_t HksAddBatchTimeToOperation(const struct HksParamSet *paramSet, struct HksOperation *operation)
262 {
263     if (paramSet == NULL || operation == NULL) {
264         return HKS_ERROR_NULL_POINTER;
265     }
266     uint64_t curTime = 0;
267     int32_t ret = HksElapsedRealTime(&curTime);
268     HKS_IF_NOT_SUCC_LOGE_RETURN(ret, ret, "HksElapsedRealTime failed")
269     bool findOperation = false;
270     bool findTimeout = false;
271     operation->isBatchOperation = false;
272     operation->batchOperationTimestamp = curTime + DEFAULT_BATCH_TIME_OUT * S_TO_MS;
273     for (uint32_t i = 0; i < paramSet->paramsCnt; i++) {
274         if (paramSet->params[i].tag == HKS_TAG_IS_BATCH_OPERATION) {
275             operation->isBatchOperation = paramSet->params[i].boolParam;
276             findOperation = true;
277             continue;
278         }
279         if (paramSet->params[i].tag == HKS_TAG_BATCH_OPERATION_TIMEOUT) {
280             if ((uint64_t)paramSet->params[i].uint32Param > MAX_BATCH_TIME_OUT) {
281                 HKS_LOG_E("Batch time is too big.");
282                 return HKS_ERROR_NOT_SUPPORTED;
283             }
284             operation->batchOperationTimestamp = curTime + (uint64_t)paramSet->params[i].uint32Param * S_TO_MS;
285             findTimeout = true;
286             continue;
287         }
288         if (findOperation && findTimeout) {
289             break;
290         }
291     }
292     if (!findOperation) {
293         operation->batchOperationTimestamp = 0;
294     }
295     return HKS_SUCCESS;
296 }
297 
CreateOperation(const struct HksProcessInfo * processInfo,const struct HksParamSet * paramSet,const struct HksBlob * operationHandle,bool abortable)298 int32_t CreateOperation(const struct HksProcessInfo *processInfo, const struct HksParamSet *paramSet,
299     const struct HksBlob *operationHandle, bool abortable)
300 {
301     struct HksOperation *operation = (struct HksOperation *)HksMalloc(sizeof(struct HksOperation));
302     HKS_IF_NULL_LOGE_RETURN(operation, HKS_ERROR_MALLOC_FAIL, "malloc hks operation failed")
303 
304     int32_t ret = ConstructOperationProcessInfo(processInfo, operation);
305     if (ret != HKS_SUCCESS) {
306         HKS_LOG_E("constrtct operation process info failed");
307         HKS_FREE(operation);
308         return ret;
309     }
310 
311     ret = ConstructOperationHandle(operationHandle, &(operation->handle));
312     if (ret != HKS_SUCCESS) {
313         HKS_LOG_E("constrtct operation handle failed");
314         HKS_FREE_BLOB(operation->processInfo.processName);
315         HKS_FREE_BLOB(operation->processInfo.userId);
316         HKS_FREE(operation);
317         return ret;
318     }
319 
320     operation->abortable = abortable;
321     operation->isInUse = false;
322 
323     if (paramSet != NULL) {
324         ret = HksAddBatchTimeToOperation(paramSet, operation);
325         if (ret != HKS_SUCCESS) {
326             HKS_LOG_E("constrtct operation handle failed");
327             HKS_FREE_BLOB(operation->processInfo.processName);
328             HKS_FREE_BLOB(operation->processInfo.userId);
329             HKS_FREE(operation);
330             return ret;
331         }
332     }
333 
334     struct HksParam *specificUserIdParam = NULL;
335     if (HksGetParam(paramSet, HKS_TAG_SPECIFIC_USER_ID, &specificUserIdParam) == HKS_SUCCESS) {
336         operation->isUserIdPassedDuringInit = true;
337         operation->userIdPassedDuringInit = specificUserIdParam->int32Param;
338     }
339 
340     ret = AddOperation(operation);
341     if (ret != HKS_SUCCESS) {
342         HKS_FREE_BLOB(operation->processInfo.processName);
343         HKS_FREE_BLOB(operation->processInfo.userId);
344         HKS_FREE(operation);
345     }
346 
347     return ret;
348 }
349 
IsSameProcessName(const struct HksProcessInfo * processInfo,const struct HksOperation * operation)350 static bool IsSameProcessName(const struct HksProcessInfo *processInfo, const struct HksOperation *operation)
351 {
352     uint32_t processNameLen = operation->processInfo.processName.size;
353     return ((processNameLen == processInfo->processName.size) &&
354         (memcmp(operation->processInfo.processName.data, processInfo->processName.data, processNameLen) == 0));
355 }
356 
IsSameUserId(const struct HksProcessInfo * processInfo,const struct HksOperation * operation)357 static bool IsSameUserId(const struct HksProcessInfo *processInfo, const struct HksOperation *operation)
358 {
359     uint32_t userIdLen = operation->processInfo.userId.size;
360     return ((userIdLen == processInfo->userId.size) &&
361         (memcmp(operation->processInfo.userId.data, processInfo->userId.data, userIdLen) == 0));
362 }
363 
QueryOperationAndMarkInUse(const struct HksProcessInfo * processInfo,const struct HksBlob * operationHandle)364 struct HksOperation *QueryOperationAndMarkInUse(const struct HksProcessInfo *processInfo,
365     const struct HksBlob *operationHandle)
366 {
367     uint64_t handle;
368     int32_t ret = ConstructOperationHandle(operationHandle, &handle);
369     HKS_IF_NOT_SUCC_LOGE_RETURN(ret, NULL, "construct handle failed when query operation")
370 
371     struct HksOperation *operation = NULL;
372     pthread_mutex_lock(&g_lock);
373     HKS_DLIST_ITER(operation, &g_operationList) {
374         if ((operation != NULL) && (operation->handle == handle) && IsSameProcessName(processInfo, operation) &&
375             IsSameUserId(processInfo, operation)) {
376             if (operation->isInUse) {
377                 HKS_LOG_E("operation is in use!");
378                 pthread_mutex_unlock(&g_lock);
379                 return NULL;
380             }
381             operation->isInUse = true;
382             pthread_mutex_unlock(&g_lock);
383             return operation;
384         }
385     }
386     pthread_mutex_unlock(&g_lock);
387 
388     return NULL;
389 }
390 
MarkOperationUnUse(struct HksOperation * operation)391 void MarkOperationUnUse(struct HksOperation *operation)
392 {
393     if (operation == NULL) {
394         return;
395     }
396     operation->isInUse = false;
397 }
398 
DeleteOperation(const struct HksBlob * operationHandle)399 void DeleteOperation(const struct HksBlob *operationHandle)
400 {
401     uint64_t handle;
402     int32_t ret = ConstructOperationHandle(operationHandle, &handle);
403     if (ret != HKS_SUCCESS) {
404         HKS_LOG_E("construct handle failed when delete operation");
405         return;
406     }
407 
408     struct HksOperation *operation = NULL;
409     pthread_mutex_lock(&g_lock);
410     HKS_DLIST_ITER(operation, &g_operationList) {
411         if (operation != NULL && operation->handle == handle) {
412             if (operation->isInUse) {
413                 HKS_LOG_I("operation is in use, do not delete");
414                 break;
415             }
416             FreeOperation(&operation);
417             --g_operationCount;
418             HKS_LOG_D("delete operation count:%" LOG_PUBLIC "u", g_operationCount);
419             pthread_mutex_unlock(&g_lock);
420             return;
421         }
422     }
423     pthread_mutex_unlock(&g_lock);
424 }
425 
DeleteSession(const struct HksProcessInfo * processInfo,struct HksOperation * operation)426 static void DeleteSession(const struct HksProcessInfo *processInfo, struct HksOperation *operation)
427 {
428     if (operation->isInUse) {
429         HKS_LOG_E("operation is in use, do not delete");
430         return;
431     }
432     bool isNeedDelete = false;
433     if (processInfo->processName.size == 0) { /* delete by user id */
434         isNeedDelete = IsSameUserId(processInfo, operation);
435     } else { /* delete by process name */
436         isNeedDelete = IsSameUserId(processInfo, operation) && IsSameProcessName(processInfo, operation);
437     }
438 
439     if (isNeedDelete) {
440         DeleteKeyNodeAndDecreaseGlobalCount(operation);
441     }
442 }
443 
DeleteSessionByProcessInfo(const struct HksProcessInfo * processInfo)444 void DeleteSessionByProcessInfo(const struct HksProcessInfo *processInfo)
445 {
446     struct HksOperation *operation = NULL;
447 
448     pthread_mutex_lock(&g_lock);
449     HKS_DLIST_SAFT_ITER(operation, &g_operationList) {
450         if (operation != NULL) {
451             DeleteSession(processInfo, operation);
452         }
453     }
454     pthread_mutex_unlock(&g_lock);
455 }
456