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(¶mSet) != HKS_SUCCESS) {
59 HKS_FREE(handle);
60 return;
61 }
62
63 (void)HuksAccessAbort(&handleBlob, paramSet);
64
65 HksFreeParamSet(¶mSet);
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