1 /*
2 * Copyright (c) 2023-2025 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 "cert_manager_updateflag.h"
17
18 #include <dirent.h>
19 #include <libgen.h>
20 #include <openssl/x509.h>
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <unistd.h>
24 #include <string.h>
25
26 #include "cert_manager.h"
27 #include "cert_manager_file_operator.h"
28 #include "cert_manager_mem.h"
29 #include "cert_manager_service.h"
30 #include "cert_manager_storage.h"
31 #include "cert_manager_uri.h"
32 #include "cm_log.h"
33 #include "cm_x509.h"
34 #include "securec.h"
35 #include "cm_util.h"
36
37 #ifdef __cplusplus
38 extern "C" {
39 #endif
40
41 static const char UPDATE_FLAG_DIR_PATH[] = "/data/service/el1/public/cert_manager_service/certificates/user";
42 static const char UPDATE_FLAG_FILE_NAME[] = "update.flag";
43
44 // LCOV_EXCL_START
45 enum UpdateFlagEnum {
46 NEED_UPDATE = '0',
47 ALREADY_UPDATE = '1',
48 };
49
50 /**
51 * @brief Get the update flag
52 *
53 * @param[out] updateFlag Used to return the update flag
54 * @return int32_t Get results
55 * @retval 0 Success
56 * @retval <0 Failure
57 */
GetUpdateFlag(uint8_t * updateFlag)58 static int32_t GetUpdateFlag(uint8_t *updateFlag)
59 {
60 uint8_t updateFlagTmp = false;
61
62 if (updateFlag == NULL) {
63 CM_LOG_E("input params is invalid");
64 return CMR_ERROR_INVALID_ARGUMENT;
65 }
66
67 /* Read the update flag */
68 uint32_t readSize =
69 CmFileRead(UPDATE_FLAG_DIR_PATH, UPDATE_FLAG_FILE_NAME, 0, &updateFlagTmp, sizeof(updateFlagTmp));
70 if (readSize == 0) {
71 CM_LOG_D("Read updateFlag file failed, the updateFlag counts as false");
72 *updateFlag = false;
73 } else if (readSize == sizeof(updateFlagTmp)) {
74 *updateFlag = updateFlagTmp;
75 } else {
76 CM_LOG_E("Failed read UpdateFlag");
77 return CMR_ERROR_STORAGE;
78 }
79
80 return CM_SUCCESS;
81 }
82
83 /**
84 * @brief Set the update flag
85 *
86 * @param[out] updateFlag Set the update flag value
87 * @return int32_t Set result
88 * @retval 0 Success
89 * @retval <0 Failure
90 */
SetUpdateFlag(uint8_t updateFlag)91 static int32_t SetUpdateFlag(uint8_t updateFlag)
92 {
93 /* Create an update flag directory */
94 if (CmMakeDir(UPDATE_FLAG_DIR_PATH) == CMR_ERROR_MAKE_DIR_FAIL) {
95 CM_LOG_E("Failed to create UPDATE_FLAG_DIR_PATH");
96 return CMR_ERROR_MAKE_DIR_FAIL;
97 }
98
99 /* Writes the update flag */
100 int32_t ret = CmFileWrite(UPDATE_FLAG_DIR_PATH, UPDATE_FLAG_FILE_NAME, 0, &updateFlag, sizeof(updateFlag));
101 if (ret != CMR_OK) {
102 CM_LOG_E("Failed to write updateFlag");
103 }
104 return ret;
105 }
106
IsCertNeedBackup(uint32_t userId,uint32_t uid,const struct CmBlob * certUri,bool * needUpdate)107 int32_t IsCertNeedBackup(uint32_t userId, uint32_t uid, const struct CmBlob *certUri, bool *needUpdate)
108 {
109 int32_t ret = CM_SUCCESS;
110 char configPath[CERT_MAX_PATH_LEN] = { 0 };
111
112 if (needUpdate == NULL) {
113 CM_LOG_E("input params is invalid");
114 return CMR_ERROR_INVALID_ARGUMENT;
115 }
116
117 ret = CmGetCertConfPath(userId, uid, certUri, configPath, CERT_MAX_PATH_LEN);
118 if (ret != CM_SUCCESS) {
119 CM_LOG_E("Construct cert config configPath failed.");
120 return ret;
121 }
122
123 do {
124 ret = CmIsFileExist(NULL, (const char *)configPath);
125 if (ret != CM_SUCCESS) {
126 if (ret != CMR_ERROR_NOT_EXIST) {
127 CM_LOG_E("check cert config file return err code: %d.", ret);
128 }
129 /* The cert config file does not exist or cannot be determined, need to
130 * backup cert */
131 *needUpdate = true;
132 break;
133 }
134 uint32_t size = 0;
135 char backupPath[CERT_MAX_PATH_LEN] = { 0 };
136 size = CmFileRead(NULL, configPath, 0, (uint8_t *)backupPath, CERT_MAX_PATH_LEN - 1);
137 if (size == 0) {
138 CM_LOG_E("read cert backup file path from configPath failed.");
139 *needUpdate = true;
140 break;
141 }
142
143 ret = CmIsFileExist(NULL, (const char *)backupPath);
144 if (ret == CMR_OK) {
145 *needUpdate = false;
146 break;
147 } else if (ret != CMR_ERROR_NOT_EXIST) {
148 CM_LOG_E("check cert backup file return err code: %d.", ret);
149 }
150 *needUpdate = true;
151 } while (0);
152
153 return CM_SUCCESS;
154 }
155
CmReadCertData(uint32_t store,const struct CmContext * context,const struct CmBlob * certUri,struct CmBlob * userCertData)156 int32_t CmReadCertData(uint32_t store, const struct CmContext *context, const struct CmBlob *certUri,
157 struct CmBlob *userCertData)
158 {
159 int32_t ret = CM_SUCCESS;
160 char uriStr[CERT_MAX_PATH_LEN] = { 0 };
161 char uidPath[CERT_MAX_PATH_LEN] = { 0 };
162
163 /* Construct certificate path */
164 ret = ConstructUidPath(context, store, uidPath, CERT_MAX_PATH_LEN);
165 if (ret != CM_SUCCESS) {
166 return ret;
167 }
168
169 if (snprintf_s(uriStr, CERT_MAX_PATH_LEN, CERT_MAX_PATH_LEN - 1, "%.*s", certUri->size, certUri->data) < 0) {
170 CM_LOG_E("Construct cert uri string failed.");
171 return CMR_ERROR_MEM_OPERATION_PRINT;
172 }
173
174 /* Reading certificate data */
175 ret = CmStorageGetBuf(uidPath, uriStr, userCertData);
176 if (ret != CM_SUCCESS) {
177 CM_LOG_E("Failed to get certificate data, ret = %d", ret);
178 return ret;
179 }
180
181 return CM_SUCCESS;
182 }
183
ConvertCertDataToPem(const struct CmBlob * userCertData,const X509 * userCertX509,struct CmBlob * userCertPemData,bool * userCertPemDataNeedFree)184 static int32_t ConvertCertDataToPem(const struct CmBlob *userCertData, const X509 *userCertX509,
185 struct CmBlob *userCertPemData, bool *userCertPemDataNeedFree)
186 {
187 if (userCertData->data[0] != '-') {
188 int32_t ret = CmX509ToPEM(userCertX509, userCertPemData);
189 if (ret != CM_SUCCESS) {
190 CM_LOG_E("CmX509ToPEM fail");
191 return ret;
192 }
193 *userCertPemDataNeedFree = true;
194 } else {
195 userCertPemData->data = userCertData->data;
196 userCertPemData->size = userCertData->size;
197 *userCertPemDataNeedFree = false;
198 }
199
200 return CM_SUCCESS;
201 }
202
CmConstructContextFromUri(const char * certUri,struct CmContext * context)203 int32_t CmConstructContextFromUri(const char *certUri, struct CmContext *context)
204 {
205 if ((certUri == NULL) || (context == NULL)) {
206 CM_LOG_E("input params is invaild");
207 return CMR_ERROR_INVALID_ARGUMENT;
208 }
209
210 struct CMUri cmUri = { 0 };
211 int32_t ret = CertManagerUriDecode(&cmUri, certUri);
212 if ((ret != CM_SUCCESS)) {
213 CM_LOG_E("Failed to decode struct CMUri from certUri, ret = %d", ret);
214 return ret;
215 }
216
217 do {
218 if ((cmUri.user == NULL) || (cmUri.app == NULL) || (cmUri.object == NULL)) {
219 CM_LOG_E("cmUri.user or cmUri.app or cmUri.object is NULL error");
220 ret = CMR_ERROR_INVALID_ARGUMENT_URI;
221 break;
222 }
223
224 if (CmIsNumeric(cmUri.user, strlen(cmUri.user) + 1, &(context->userId)) != CM_SUCCESS ||
225 CmIsNumeric(cmUri.app, strlen(cmUri.app) + 1, &(context->uid)) != CM_SUCCESS) {
226 CM_LOG_E("parse string to uint32 failed.");
227 ret = CMR_ERROR_INVALID_ARGUMENT_URI;
228 break;
229 }
230
231 if (snprintf_s(context->packageName, sizeof(context->packageName), sizeof(context->packageName) - 1, "%s",
232 cmUri.object) < 0) {
233 CM_LOG_E("Failed to fill context->packageName");
234 ret = CMR_ERROR_MEM_OPERATION_PRINT;
235 break;
236 }
237 } while (0);
238
239 (void)CertManagerFreeUri(&cmUri);
240
241 return ret;
242 }
243
BackupUserCert(const X509 * userCertX509,const struct CmBlob * userCert,const struct CmContext * context,const struct CmBlob * certUri)244 static int32_t BackupUserCert(const X509 *userCertX509, const struct CmBlob *userCert, const struct CmContext *context,
245 const struct CmBlob *certUri)
246 {
247 char userCertConfigFilePath[CERT_MAX_PATH_LEN] = { 0 };
248 char userCertBackupFilePath[CERT_MAX_PATH_LEN] = { 0 };
249
250 int32_t ret = CmGetCertConfPath(context->userId, context->uid, certUri, userCertConfigFilePath, CERT_MAX_PATH_LEN);
251 if (ret != CM_SUCCESS) {
252 CM_LOG_E("CmGetCertConfPath fail");
253 return ret;
254 }
255
256 ret = CmRemoveBackupUserCert(context, certUri, userCertConfigFilePath);
257 if (ret != CMR_OK) {
258 CM_LOG_E("Remove user cert config and backup file failed, ret: %d", ret);
259 }
260
261 ret = CmGetCertBackupFilePath(userCertX509, context->userId, userCertBackupFilePath, CERT_MAX_PATH_LEN);
262 if (ret != CM_SUCCESS) {
263 CM_LOG_E("CmGetCertBackupFilePath fail");
264 return ret;
265 }
266 ret = CmGenerateSaConf(userCertConfigFilePath, NULL, userCertBackupFilePath);
267 if (ret != CM_SUCCESS) {
268 CM_LOG_E("GenerateSaConf: save CertBackupFilePath fail");
269 return ret;
270 }
271
272 ret = CmStoreUserCert(NULL, userCert, userCertBackupFilePath);
273 if (ret != CM_SUCCESS) {
274 CM_LOG_E("StoreUserCert fail");
275 return ret;
276 }
277
278 return CM_SUCCESS;
279 }
280
CmBackupUserCert(const struct CmContext * context,const struct CmBlob * certUri,const struct CmBlob * certData)281 int32_t CmBackupUserCert(const struct CmContext *context, const struct CmBlob *certUri, const struct CmBlob *certData)
282 {
283 if ((context == NULL) || (CmCheckBlob(certUri) != CM_SUCCESS) || (CmCheckBlob(certData) != CM_SUCCESS)) {
284 CM_LOG_E("Invalid input arguments");
285 return CMR_ERROR_INVALID_ARGUMENT;
286 }
287
288 X509 *userCertX509 = InitCertContext(certData->data, certData->size);
289 if (userCertX509 == NULL) {
290 CM_LOG_E("Parse X509 cert fail");
291 return CMR_ERROR_INVALID_CERT_FORMAT;
292 }
293
294 int32_t ret = CM_SUCCESS;
295 struct CmBlob certPemData = { 0, NULL };
296 bool certPemDataNeedFree = false;
297 do {
298 ret = ConvertCertDataToPem(certData, userCertX509, &certPemData, &certPemDataNeedFree);
299 if (ret != CM_SUCCESS) {
300 CM_LOG_E("ConvertCertDataToPem fail");
301 break;
302 }
303
304 ret = BackupUserCert(userCertX509, (const struct CmBlob *)&certPemData, context, certUri);
305 if (ret != CM_SUCCESS) {
306 CM_LOG_E("BackupUserCert fail");
307 break;
308 }
309 } while (0);
310
311 if (certPemDataNeedFree == true)
312 CM_FREE_BLOB(certPemData);
313
314 FreeCertContext(userCertX509);
315
316 return ret;
317 }
318
UpdateUserCert(uint32_t userId,uint32_t uid,const char * certPath)319 static int32_t UpdateUserCert(uint32_t userId, uint32_t uid, const char *certPath)
320 {
321 int32_t ret = CM_SUCCESS;
322 char *uriStr = NULL;
323 struct CmBlob certUri = { 0 };
324
325 if (certPath == NULL) {
326 CM_LOG_E("input params is invaild");
327 return CMR_ERROR_INVALID_ARGUMENT;
328 }
329 uriStr = basename((char *)certPath);
330 certUri.data = (uint8_t *)uriStr;
331 certUri.size = strlen(uriStr);
332
333 bool needUpdate = false;
334 ret = IsCertNeedBackup(userId, uid, &certUri, &needUpdate);
335 if (ret != CM_SUCCESS) {
336 CM_LOG_E("Check cert is need update failed, ret = %d", ret);
337 return ret;
338 } else if (needUpdate == false) {
339 /* No need to update */
340 return CM_SUCCESS;
341 }
342
343 struct CmContext context = { 0 };
344 ret = CmConstructContextFromUri((const char *)uriStr, &context);
345 if (ret != CM_SUCCESS) {
346 CM_LOG_E("ConstructContextFromUri failed, ret = %d", ret);
347 return CM_FAILURE;
348 }
349
350 uint32_t store = CM_USER_TRUSTED_STORE;
351 struct CmBlob certificateData = { 0, NULL };
352 ret = CmReadCertData(store, &context, &certUri, &certificateData);
353 if (ret != CM_SUCCESS) {
354 CM_LOG_E("CmReadCertData failed, ret = %d", ret);
355 return CM_FAILURE;
356 }
357
358 ret = CmBackupUserCert(&context, &certUri, &certificateData);
359 if (ret != CM_SUCCESS) {
360 CM_LOG_E("update user certUri failed, ret = %d", ret);
361 ret = CM_FAILURE;
362 }
363
364 CM_FREE_BLOB(certificateData);
365
366 return ret;
367 }
368
UpdateUserCerts(uint32_t userId,const char * userIdPath)369 static int32_t UpdateUserCerts(uint32_t userId, const char *userIdPath)
370 {
371 DIR *dir = opendir(userIdPath);
372 if (dir == NULL) {
373 CM_LOG_E("opendir userIdPath failed");
374 return CM_FAILURE;
375 }
376
377 struct dirent *dire = NULL;
378 /* Traverse the user/{userId} directory */
379 while ((dire = readdir(dir)) != NULL) {
380 if ((strcmp(dire->d_name, ".") == 0) || (strcmp(dire->d_name, "..") == 0)) {
381 continue;
382 }
383 char uidPath[CERT_MAX_PATH_LEN] = { 0 };
384 if (snprintf_s(uidPath, CERT_MAX_PATH_LEN, CERT_MAX_PATH_LEN - 1, "%s/%s", userIdPath, dire->d_name) < 0) {
385 CM_LOG_E("Construct userId path failed");
386 continue;
387 }
388
389 int32_t ret = 0;
390 uint32_t fileCounts = 0;
391 struct CmBlob fileNames[MAX_COUNT_CERTIFICATE] = { 0 };
392 /* Gets all files under the uidPath */
393 ret = CmUidLayerGetFileCountAndNames(uidPath, fileNames, MAX_COUNT_CERTIFICATE, &fileCounts);
394 if (ret != CM_SUCCESS) {
395 CM_LOG_E("Get file count and names from path of userId layer failed");
396 continue;
397 }
398
399 /* Traverse all files under the uidPath */
400 for (uint32_t i = 0; i < fileCounts; i++) {
401 struct CmBlob *certFilePath = &fileNames[i];
402
403 uint32_t uid = 0;
404 /* Update certificate file */
405 if (CmIsNumeric(dire->d_name, strlen(dire->d_name) + 1, &uid) != CM_SUCCESS) {
406 CM_LOG_E("parse string to uint32 failed.");
407 continue;
408 }
409
410 ret = UpdateUserCert(userId, uid, (const char *)certFilePath->data);
411 if (ret != CM_SUCCESS) {
412 CM_LOG_E("Failed to update cert file for the certFilePath");
413 continue;
414 }
415 }
416
417 CmFreeFileNames(fileNames, fileCounts);
418 };
419
420 closedir(dir);
421
422 return CM_SUCCESS;
423 }
424
UpdateAllUserCerts(void)425 static int32_t UpdateAllUserCerts(void)
426 {
427 DIR *dir = NULL;
428 struct dirent *dire = NULL;
429 uint32_t userId = 0;
430 char userIdPath[CERT_MAX_PATH_LEN] = { 0 };
431
432 /* do nothing when dir is not exist */
433 if (CmIsDirExist(USER_CA_STORE) != CMR_OK) {
434 CM_LOG_D("Root dir is not exist");
435 return CM_SUCCESS;
436 }
437
438 if ((dir = opendir(USER_CA_STORE)) == NULL) {
439 CM_LOG_E("open USER_CA_STORE dir failed");
440 return CM_FAILURE;
441 }
442
443 /* Traverse the user directory */
444 while ((dire = readdir(dir)) != NULL) {
445 if ((dire->d_type != DT_DIR) || (strcmp(dire->d_name, ".") == 0) || (strcmp(dire->d_name, "..") == 0)) {
446 /* If it is not a directory or a special directory, skip it */
447 continue;
448 }
449
450 if (snprintf_s(userIdPath, CERT_MAX_PATH_LEN, CERT_MAX_PATH_LEN - 1, "%s%s", USER_CA_STORE, dire->d_name) < 0) {
451 CM_LOG_E("Construct userId path failed");
452 continue;
453 }
454
455 /* Updates all certificates for the specified user */
456 if (CmIsNumeric(dire->d_name, strlen(dire->d_name) + 1, &userId) != CM_SUCCESS) {
457 CM_LOG_E("parse string to uint32 failed.");
458 continue;
459 }
460
461 int32_t ret = UpdateUserCerts(userId, userIdPath);
462 if (ret != CM_SUCCESS) {
463 CM_LOG_E("Failed to update all certificates for the userIdPath");
464 continue;
465 }
466 };
467
468 closedir(dir);
469
470 return CM_SUCCESS;
471 }
472
CmBackupAllSaUserCerts(void)473 int32_t CmBackupAllSaUserCerts(void)
474 {
475 int32_t ret = 0;
476 uint8_t updateFlag = 0;
477
478 /* Obtain the update flag */
479 ret = GetUpdateFlag(&updateFlag);
480 if (ret != CM_SUCCESS) {
481 CM_LOG_E("GetUpdateFlag failed");
482 return ret;
483 }
484
485 if (updateFlag == ALREADY_UPDATE) {
486 CM_LOG_D("updateFlag is ALREADY_UPDATE, so not need update");
487 return CM_SUCCESS;
488 }
489
490 /* Update all certificate files */
491 ret = UpdateAllUserCerts();
492 if (ret != CM_SUCCESS) {
493 CM_LOG_E("UpdateAllUserCerts failed");
494 return ret;
495 }
496
497 /* Set the Update flag */
498 ret = SetUpdateFlag(ALREADY_UPDATE);
499 if (ret != CM_SUCCESS) {
500 CM_LOG_E("SetUpdateFlag failed");
501 return ret;
502 }
503
504 return CM_SUCCESS;
505 }
506
507 #ifdef __cplusplus
508 }
509 #endif
510 // LCOV_EXCL_STOP