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