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