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