1 /*
2  * Copyright (c) 2022-2024 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 "cm_napi_grant.h"
17 
18 #include "securec.h"
19 
20 #include "cert_manager_api.h"
21 #include "cm_log.h"
22 #include "cm_mem.h"
23 #include "cm_type.h"
24 #include "cm_napi_common.h"
25 #include "cm_util.h"
26 
27 namespace CMNapi {
28 namespace {
29 constexpr int CM_NAPI_GRANT_ARGS_CNT = 3;
30 constexpr int CM_NAPI_IS_AUTHED_ARGS_CNT = 2;
31 constexpr int CM_NAPI_CALLBACK_ARG_CNT = 1;
32 
33 constexpr uint32_t OUT_AUTH_URI_SIZE = 1000;
34 constexpr uint32_t OUT_AUTH_LIST_SIZE = 512;
35 constexpr uint32_t OUT_UINT32_STRING_SIZE = 16;
36 } // namespace
37 
38 struct GrantAsyncContextT {
39     napi_async_work asyncWork = nullptr;
40     napi_deferred deferred = nullptr;
41     napi_ref callback = nullptr;
42 
43     int32_t errCode = 0;
44     uint32_t appUid = 0;
45     struct CmBlob *keyUri = nullptr;
46     struct CmBlob *authUri = nullptr;
47     struct CmAppUidList *uidList = nullptr;
48 };
49 using GrantAsyncContext = GrantAsyncContextT *;
50 
InitGrantAsyncContext(void)51 static GrantAsyncContext InitGrantAsyncContext(void)
52 {
53     GrantAsyncContext context = static_cast<GrantAsyncContext>(CmMalloc(sizeof(GrantAsyncContextT)));
54     if (context != nullptr) {
55         (void)memset_s(context, sizeof(GrantAsyncContextT), 0, sizeof(GrantAsyncContextT));
56     }
57     return context;
58 }
59 
FreeGrantAsyncContext(napi_env env, GrantAsyncContext &context)60 static void FreeGrantAsyncContext(napi_env env, GrantAsyncContext &context)
61 {
62     if (context == nullptr) {
63         return;
64     }
65 
66     DeleteNapiContext(env, context->asyncWork, context->callback);
67     FreeCmBlob(context->keyUri);
68     FreeCmBlob(context->authUri);
69     if (context->uidList != nullptr) {
70         CM_FREE_PTR(context->uidList->appUid);
71         CM_FREE_PTR(context->uidList);
72     }
73     CM_FREE_PTR(context);
74 }
75 
ParseString2Uint32(napi_env env, napi_value object, uint32_t &value)76 static napi_value ParseString2Uint32(napi_env env, napi_value object, uint32_t &value)
77 {
78     struct CmBlob *blob = nullptr;
79     napi_value result = ParseString(env, object, blob);
80     if (result == nullptr ||
81         CmIsNumeric(reinterpret_cast<char *>(blob->data), static_cast<size_t>(blob->size), &value) != CM_SUCCESS) {
82         CM_LOG_E("parse string to uint32 failed");
83         if (blob != nullptr) {
84             CM_FREE_PTR(blob->data);
85             CmFree(blob);
86         }
87         return nullptr;
88     }
89 
90     CM_FREE_PTR(blob->data);
91     CM_FREE_PTR(blob);
92     return GetInt32(env, 0);
93 }
94 
ParseGrantUidParams(napi_env env, napi_callback_info info, GrantAsyncContext context)95 static napi_value ParseGrantUidParams(napi_env env, napi_callback_info info, GrantAsyncContext context)
96 {
97     size_t argc = CM_NAPI_GRANT_ARGS_CNT;
98     napi_value argv[CM_NAPI_GRANT_ARGS_CNT] = { nullptr };
99     NAPI_CALL(env, napi_get_cb_info(env, info, &argc, argv, nullptr, nullptr));
100 
101     if ((argc != CM_NAPI_GRANT_ARGS_CNT) && (argc != (CM_NAPI_GRANT_ARGS_CNT - CM_NAPI_CALLBACK_ARG_CNT))) {
102         ThrowError(env, PARAM_ERROR, "arguments count invalid when grant or remove uid");
103         CM_LOG_E("arguments count is not expected when grant or remove uid");
104         return nullptr;
105     }
106 
107     size_t index = 0;
108     napi_value result = ParseString(env, argv[index], context->keyUri);
109     if (result == nullptr) {
110         ThrowError(env, PARAM_ERROR, "keyUri type error");
111         CM_LOG_E("get uri failed when grant or remove uid");
112         return nullptr;
113     }
114 
115     index++;
116     result = ParseString2Uint32(env, argv[index], context->appUid);
117     if (result == nullptr) {
118         ThrowError(env, PARAM_ERROR, "appUid type error");
119         CM_LOG_E("get app uid failed when grant or remove uid ");
120         return nullptr;
121     }
122 
123     index++;
124     if (index < argc) {
125         int32_t ret = GetCallback(env, argv[index], context->callback);
126         if (ret != CM_SUCCESS) {
127             ThrowError(env, PARAM_ERROR, "Get callback type failed.");
128             CM_LOG_E("get callback function failed when grant or remove uid");
129             return nullptr;
130         }
131     }
132 
133     return GetInt32(env, 0);
134 }
135 
ParseRemoveUidParams(napi_env env, napi_callback_info info, GrantAsyncContext context)136 static napi_value ParseRemoveUidParams(napi_env env, napi_callback_info info, GrantAsyncContext context)
137 {
138     return ParseGrantUidParams(env, info, context);
139 }
140 
ParseIsAuthedParams(napi_env env, napi_callback_info info, GrantAsyncContext context)141 static napi_value ParseIsAuthedParams(napi_env env, napi_callback_info info, GrantAsyncContext context)
142 {
143     size_t argc = CM_NAPI_IS_AUTHED_ARGS_CNT;
144     napi_value argv[CM_NAPI_IS_AUTHED_ARGS_CNT] = { nullptr };
145     NAPI_CALL(env, napi_get_cb_info(env, info, &argc, argv, nullptr, nullptr));
146 
147     if ((argc != CM_NAPI_IS_AUTHED_ARGS_CNT) && (argc != (CM_NAPI_IS_AUTHED_ARGS_CNT - CM_NAPI_CALLBACK_ARG_CNT))) {
148         ThrowError(env, PARAM_ERROR, "arguments count invalid, arguments count need between 1 and 2.");
149         CM_LOG_E("arguments count is not expected when using isAuthed");
150         return nullptr;
151     }
152 
153     size_t index = 0;
154     napi_value result = ParseString(env, argv[index], context->keyUri);
155     if (result == nullptr) {
156         ThrowError(env, PARAM_ERROR, "keyUri is not a string or the length is 0 or too long.");
157         CM_LOG_E("get uri failed when using isAuthed");
158         return nullptr;
159     }
160 
161     index++;
162     if (index < argc) {
163         int32_t ret = GetCallback(env, argv[index], context->callback);
164         if (ret != CM_SUCCESS) {
165             ThrowError(env, PARAM_ERROR, "Get callback failed, callback must be a function.");
166             CM_LOG_E("get callback function failed when using isAuthed");
167             return nullptr;
168         }
169     }
170 
171     return GetInt32(env, 0);
172 }
173 
174 
ParseGetUidListParams(napi_env env, napi_callback_info info, GrantAsyncContext context)175 static napi_value ParseGetUidListParams(napi_env env, napi_callback_info info, GrantAsyncContext context)
176 {
177     return ParseIsAuthedParams(env, info, context);
178 }
179 
GrantUidExecute(napi_env env, void *data)180 static void GrantUidExecute(napi_env env, void *data)
181 {
182     GrantAsyncContext context = static_cast<GrantAsyncContext>(data);
183     context->authUri = static_cast<CmBlob *>(CmMalloc(sizeof(CmBlob)));
184     if (context->authUri == nullptr) {
185         CM_LOG_E("malloc authUri failed");
186         context->errCode = CMR_ERROR_MALLOC_FAIL;
187         return;
188     }
189     (void)memset_s(context->authUri, sizeof(CmBlob), 0, sizeof(CmBlob));
190 
191     context->authUri->data = static_cast<uint8_t *>(CmMalloc(OUT_AUTH_URI_SIZE));
192     if (context->authUri->data == nullptr) {
193         CM_LOG_E("malloc authUri.data failed");
194         context->errCode = CMR_ERROR_MALLOC_FAIL;
195         return;
196     }
197     (void)memset_s(context->authUri->data, OUT_AUTH_URI_SIZE, 0, OUT_AUTH_URI_SIZE);
198     context->authUri->size = OUT_AUTH_URI_SIZE;
199 
200     context->errCode = CmGrantAppCertificate(context->keyUri, context->appUid, context->authUri);
201 }
202 
ConvertResultAuthUri(napi_env env, const CmBlob *authUri)203 static napi_value ConvertResultAuthUri(napi_env env, const CmBlob *authUri)
204 {
205     napi_value result = nullptr;
206     NAPI_CALL(env, napi_create_object(env, &result));
207 
208     napi_value authUriNapi = nullptr;
209     NAPI_CALL(env, napi_create_string_latin1(env,
210         reinterpret_cast<const char *>(authUri->data), NAPI_AUTO_LENGTH, &authUriNapi));
211     NAPI_CALL(env, napi_set_named_property(env, result, "uri", authUriNapi));
212 
213     return result;
214 }
215 
GrantUidComplete(napi_env env, napi_status status, void *data)216 static void GrantUidComplete(napi_env env, napi_status status, void *data)
217 {
218     GrantAsyncContext context = static_cast<GrantAsyncContext>(data);
219     napi_value result[RESULT_NUMBER] = { nullptr };
220     if (context->errCode == CM_SUCCESS) {
221         napi_create_uint32(env, 0, &result[0]);
222         result[1] = ConvertResultAuthUri(env, context->authUri);
223     } else {
224         result[0] = GenerateBusinessError(env, context->errCode);
225         napi_get_undefined(env, &result[1]);
226     }
227 
228     if (context->deferred != nullptr) {
229         GeneratePromise(env, context->deferred, context->errCode, result, CM_ARRAY_SIZE(result));
230     } else {
231         GenerateCallback(env, context->callback, result, CM_ARRAY_SIZE(result), context->errCode);
232     }
233     FreeGrantAsyncContext(env, context);
234 }
235 
RemoveUidExecute(napi_env env, void *data)236 static void RemoveUidExecute(napi_env env, void *data)
237 {
238     GrantAsyncContext context = static_cast<GrantAsyncContext>(data);
239     context->errCode = CmRemoveGrantedApp(context->keyUri, context->appUid);
240 }
241 
RemoveOrIsAuthedComplete(napi_env env, napi_status status, void *data)242 static void RemoveOrIsAuthedComplete(napi_env env, napi_status status, void *data)
243 {
244     GrantAsyncContext context = static_cast<GrantAsyncContext>(data);
245     napi_value result[RESULT_NUMBER] = { nullptr };
246     if (context->errCode == CM_SUCCESS) {
247         napi_create_uint32(env, 0, &result[0]);
248         napi_get_boolean(env, true, &result[1]);
249     } else if (context->errCode == CMR_ERROR_AUTH_CHECK_FAILED) {
250         napi_create_uint32(env, 0, &result[0]);
251         napi_get_boolean(env, false, &result[1]);
252         context->errCode = CM_SUCCESS;
253     } else {
254         result[0] = GenerateBusinessError(env, context->errCode);
255         napi_get_undefined(env, &result[1]);
256     }
257 
258     if (context->deferred != nullptr) {
259         GeneratePromise(env, context->deferred, context->errCode, result, CM_ARRAY_SIZE(result));
260     } else {
261         GenerateCallback(env, context->callback, result, CM_ARRAY_SIZE(result), context->errCode);
262     }
263     FreeGrantAsyncContext(env, context);
264 }
265 
IsAuthedExecute(napi_env env, void *data)266 static void IsAuthedExecute(napi_env env, void *data)
267 {
268     GrantAsyncContext context = static_cast<GrantAsyncContext>(data);
269     context->errCode = CmIsAuthorizedApp(context->keyUri);
270 }
271 
GetUidListExecute(napi_env env, void *data)272 static void GetUidListExecute(napi_env env, void *data)
273 {
274     GrantAsyncContext context = static_cast<GrantAsyncContext>(data);
275     context->uidList = static_cast<CmAppUidList *>(CmMalloc(sizeof(CmAppUidList)));
276     if (context->uidList == nullptr) {
277         CM_LOG_E("malloc uid list failed");
278         context->errCode = CMR_ERROR_MALLOC_FAIL;
279         return;
280     }
281     (void)memset_s(context->uidList, sizeof(CmAppUidList), 0, sizeof(CmAppUidList));
282 
283     uint32_t uidListSize = OUT_AUTH_LIST_SIZE * sizeof(uint32_t);
284     context->uidList->appUid = static_cast<uint32_t *>(CmMalloc(uidListSize));
285     if (context->uidList->appUid == nullptr) {
286         CM_LOG_E("malloc uid_list.appUid failed");
287         context->errCode = CMR_ERROR_MALLOC_FAIL;
288         return;
289     }
290     (void)memset_s(context->uidList->appUid, uidListSize, 0, uidListSize);
291     context->uidList->appUidCount = OUT_AUTH_LIST_SIZE;
292 
293     context->errCode = CmGetAuthorizedAppList(context->keyUri, context->uidList);
294 }
295 
ConvertResultAuthList(napi_env env, const CmAppUidList *appUidList)296 static napi_value ConvertResultAuthList(napi_env env, const CmAppUidList *appUidList)
297 {
298     napi_value result = nullptr;
299     NAPI_CALL(env, napi_create_object(env, &result));
300 
301     napi_value uidListArray = nullptr;
302     NAPI_CALL(env, napi_create_array(env, &uidListArray));
303 
304     for (uint32_t i = 0; i < appUidList->appUidCount; ++i) {
305         char uidStr[OUT_UINT32_STRING_SIZE] = {0};
306         if (snprintf_s(uidStr, sizeof(uidStr), sizeof(uidStr) - 1, "%u", appUidList->appUid[i]) < 0) {
307             CM_LOG_E("uid to string failed");
308             return result;
309         }
310 
311         napi_value element = nullptr;
312         NAPI_CALL(env, napi_create_string_latin1(env, static_cast<const char *>(uidStr), NAPI_AUTO_LENGTH, &element));
313         NAPI_CALL(env, napi_set_element(env, uidListArray, i, element));
314     }
315 
316     NAPI_CALL(env, napi_set_named_property(env, result, "appUidList", uidListArray));
317     return result;
318 }
319 
GetUidListComplete(napi_env env, napi_status status, void *data)320 static void GetUidListComplete(napi_env env, napi_status status, void *data)
321 {
322     GrantAsyncContext context = static_cast<GrantAsyncContext>(data);
323     napi_value result[RESULT_NUMBER] = { nullptr };
324     if (context->errCode == CM_SUCCESS) {
325         napi_create_uint32(env, 0, &result[0]);
326         result[1] = ConvertResultAuthList(env, context->uidList);
327     } else {
328         result[0] = GenerateBusinessError(env, context->errCode);
329         napi_get_undefined(env, &result[1]);
330     }
331 
332     if (context->deferred != nullptr) {
333         GeneratePromise(env, context->deferred, context->errCode, result, CM_ARRAY_SIZE(result));
334     } else {
335         GenerateCallback(env, context->callback, result, CM_ARRAY_SIZE(result), context->errCode);
336     }
337     FreeGrantAsyncContext(env, context);
338 }
339 
GrantUidAsyncWork(napi_env env, GrantAsyncContext context)340 static napi_value GrantUidAsyncWork(napi_env env, GrantAsyncContext context)
341 {
342     napi_value promise = nullptr;
343     GenerateNapiPromise(env, context->callback, &context->deferred, &promise);
344 
345     napi_value resourceName = nullptr;
346     NAPI_CALL(env, napi_create_string_latin1(env, "grantAppCertificate", NAPI_AUTO_LENGTH, &resourceName));
347 
348     NAPI_CALL(env, napi_create_async_work(
349         env, nullptr, resourceName,
350         GrantUidExecute,
351         GrantUidComplete,
352         static_cast<void *>(context),
353         &context->asyncWork));
354 
355     napi_status status = napi_queue_async_work(env, context->asyncWork);
356     if (status != napi_ok) {
357         ThrowError(env, INNER_FAILURE, "queue asyncWork error");
358         CM_LOG_E("get async work failed when granting uid");
359         return nullptr;
360     }
361     return promise;
362 }
363 
RemoveUidAsyncWork(napi_env env, GrantAsyncContext context)364 static napi_value RemoveUidAsyncWork(napi_env env, GrantAsyncContext context)
365 {
366     napi_value promise = nullptr;
367     GenerateNapiPromise(env, context->callback, &context->deferred, &promise);
368 
369     napi_value resourceName = nullptr;
370     NAPI_CALL(env, napi_create_string_latin1(env, "removeGrantedAppCertificate", NAPI_AUTO_LENGTH, &resourceName));
371 
372     NAPI_CALL(env, napi_create_async_work(
373         env, nullptr, resourceName,
374         RemoveUidExecute,
375         RemoveOrIsAuthedComplete,
376         static_cast<void *>(context),
377         &context->asyncWork));
378 
379     napi_status status = napi_queue_async_work(env, context->asyncWork);
380     if (status != napi_ok) {
381         ThrowError(env, INNER_FAILURE, "queue asyncWork error");
382         CM_LOG_E("queue async work failed when removing uid");
383         return nullptr;
384     }
385     return promise;
386 }
387 
IsAuthedAsyncWork(napi_env env, GrantAsyncContext context)388 static napi_value IsAuthedAsyncWork(napi_env env, GrantAsyncContext context)
389 {
390     napi_value promise = nullptr;
391     GenerateNapiPromise(env, context->callback, &context->deferred, &promise);
392 
393     napi_value resourceName = nullptr;
394     NAPI_CALL(env, napi_create_string_latin1(env, "isAuthorizedApp", NAPI_AUTO_LENGTH, &resourceName));
395 
396     NAPI_CALL(env, napi_create_async_work(
397         env, nullptr, resourceName,
398         IsAuthedExecute,
399         RemoveOrIsAuthedComplete,
400         static_cast<void *>(context),
401         &context->asyncWork));
402 
403     napi_status status = napi_queue_async_work(env, context->asyncWork);
404     if (status != napi_ok) {
405         ThrowError(env, INNER_FAILURE, "queue asyncWork error");
406         CM_LOG_E("queue async work failed when using isAuthed");
407         return nullptr;
408     }
409     return promise;
410 }
411 
GetUidListAsyncWork(napi_env env, GrantAsyncContext context)412 static napi_value GetUidListAsyncWork(napi_env env, GrantAsyncContext context)
413 {
414     napi_value promise = nullptr;
415     GenerateNapiPromise(env, context->callback, &context->deferred, &promise);
416 
417     napi_value resourceName = nullptr;
418     NAPI_CALL(env, napi_create_string_latin1(env, "getAuthorizedAppList", NAPI_AUTO_LENGTH, &resourceName));
419 
420     NAPI_CALL(env, napi_create_async_work(
421         env, nullptr, resourceName,
422         GetUidListExecute,
423         GetUidListComplete,
424         static_cast<void *>(context),
425         &context->asyncWork));
426 
427     napi_status status = napi_queue_async_work(env, context->asyncWork);
428     if (status != napi_ok) {
429         ThrowError(env, INNER_FAILURE, "queue asyncWork error");
430         CM_LOG_E("queue async work failed when getting authed uid list");
431         return nullptr;
432     }
433     return promise;
434 }
435 
CMNapiGrantPublicCertificate(napi_env env, napi_callback_info info)436 napi_value CMNapiGrantPublicCertificate(napi_env env, napi_callback_info info)
437 {
438     GrantAsyncContext context = InitGrantAsyncContext();
439     if (context == nullptr) {
440         CM_LOG_E("init grant uid context failed");
441         return nullptr;
442     }
443 
444     napi_value result = ParseGrantUidParams(env, info, context);
445     if (result == nullptr) {
446         CM_LOG_E("parse grant uid params failed");
447         FreeGrantAsyncContext(env, context);
448         return nullptr;
449     }
450 
451     result = GrantUidAsyncWork(env, context);
452     if (result == nullptr) {
453         CM_LOG_E("start grant uid async work failed");
454         FreeGrantAsyncContext(env, context);
455         return nullptr;
456     }
457 
458     return result;
459 }
460 
CMNapiIsAuthorizedApp(napi_env env, napi_callback_info info)461 napi_value CMNapiIsAuthorizedApp(napi_env env, napi_callback_info info)
462 {
463     GrantAsyncContext context = InitGrantAsyncContext();
464     if (context == nullptr) {
465         CM_LOG_E("init is authed uid context failed");
466         return nullptr;
467     }
468 
469     napi_value result = ParseIsAuthedParams(env, info, context);
470     if (result == nullptr) {
471         CM_LOG_E("parse is authed uid params failed");
472         FreeGrantAsyncContext(env, context);
473         return nullptr;
474     }
475 
476     result = IsAuthedAsyncWork(env, context);
477     if (result == nullptr) {
478         CM_LOG_E("start is authed uid async work failed");
479         FreeGrantAsyncContext(env, context);
480         return nullptr;
481     }
482 
483     return result;
484 }
485 
CMNapiGetAuthorizedAppList(napi_env env, napi_callback_info info)486 napi_value CMNapiGetAuthorizedAppList(napi_env env, napi_callback_info info)
487 {
488     GrantAsyncContext context = InitGrantAsyncContext();
489     if (context == nullptr) {
490         CM_LOG_E("init get authed uid list context failed");
491         return nullptr;
492     }
493 
494     napi_value result = ParseGetUidListParams(env, info, context);
495     if (result == nullptr) {
496         CM_LOG_E("parse get uid list params failed");
497         FreeGrantAsyncContext(env, context);
498         return nullptr;
499     }
500 
501     result = GetUidListAsyncWork(env, context);
502     if (result == nullptr) {
503         CM_LOG_E("start get uid list async work failed");
504         FreeGrantAsyncContext(env, context);
505         return nullptr;
506     }
507 
508     return result;
509 }
510 
CMNapiRemoveGrantedPublic(napi_env env, napi_callback_info info)511 napi_value CMNapiRemoveGrantedPublic(napi_env env, napi_callback_info info)
512 {
513     GrantAsyncContext context = InitGrantAsyncContext();
514     if (context == nullptr) {
515         CM_LOG_E("init remove uid context failed");
516         return nullptr;
517     }
518 
519     napi_value result = ParseRemoveUidParams(env, info, context);
520     if (result == nullptr) {
521         CM_LOG_E("parse remove uid params failed");
522         FreeGrantAsyncContext(env, context);
523         return nullptr;
524     }
525 
526     result = RemoveUidAsyncWork(env, context);
527     if (result == nullptr) {
528         CM_LOG_E("start remove uid async work failed");
529         FreeGrantAsyncContext(env, context);
530         return nullptr;
531     }
532 
533     return result;
534 }
535 }  // namespace CMNapi
536 
537