• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2021-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 #include <cerrno>
16 #include <cstdio>
17 #include <fstream>
18 #include <iostream>
19 #include <unistd.h>
20 #include <vector>
21 #include "account_error_no.h"
22 #include "account_info.h"
23 #include "account_log_wrapper.h"
24 #include "directory_ex.h"
25 #include "file_ex.h"
26 #include "account_hisysevent_adapter.h"
27 #include "iinner_os_account_manager.h"
28 #include "data_size_report_adapter.h"
29 #include "ohos_account_data_deal.h"
30 
31 namespace OHOS {
32 namespace AccountSA {
33 namespace {
34 const char ACCOUNT_CFG_FILE_NAME[] = "/account.json";
35 const char ACCOUNT_AVATAR_NAME[] = "/account_avatar";
36 const char DATADEAL_JSON_KEY_OHOSACCOUNT_NAME[] = "account_name";
37 const char DATADEAL_JSON_KEY_OHOSACCOUNT_RAW_UID[] = "raw_uid";
38 const char DATADEAL_JSON_KEY_OHOSACCOUNT_UID[] = "open_id";
39 const char DATADEAL_JSON_KEY_OHOSACCOUNT_STATUS[] = "bind_status";
40 const char DATADEAL_JSON_KEY_OHOSACCOUNT_CALLINGUID[] = "calling_uid";
41 const char DATADEAL_JSON_KEY_OHOSACCOUNT_NICKNAME[] = "account_nickname";
42 const char DATADEAL_JSON_KEY_OHOSACCOUNT_AVATAR[] = "account_avatar";
43 const char DATADEAL_JSON_KEY_OHOSACCOUNT_SCALABLEDATA[] = "account_scalableData";
44 const char DATADEAL_JSON_KEY_OHOSACCOUNT_VERSION[] = "version";
45 const char DATADEAL_JSON_KEY_USERID[] = "user_id";
46 const char DATADEAL_JSON_KEY_BIND_TIME[] = "bind_time";
47 #ifdef ENABLE_FILE_WATCHER
48 const uint32_t ALG_COMMON_SIZE = 32;
49 #endif // ENABLE_FILE_WATCHER
50 } // namespace
51 
52 #ifdef ENABLE_FILE_WATCHER
OhosAccountDataDeal(const std::string & configFileDir)53 OhosAccountDataDeal::OhosAccountDataDeal(const std::string &configFileDir)
54     : configFileDir_(configFileDir),
55     accountFileWatcherMgr_(AccountFileWatcherMgr::GetInstance())
56 {
57     accountFileOperator_ = accountFileWatcherMgr_.accountFileOperator_;
58     initOk_ = false;
59     checkCallbackFunc_ = [this](const std::string &fileName, int32_t id, uint32_t event) {
60         ACCOUNT_LOGI("inotify event = %{public}d, fileName = %{public}s", event, fileName.c_str());
61         switch (event) {
62             case IN_MODIFY: {
63                 return DealWithFileModifyEvent(fileName, id);
64             }
65             case IN_MOVE_SELF: {
66                 accountFileWatcherMgr_.RemoveFileWatcher(id, fileName);
67                 ReportOsAccountDataTampered(id, fileName, "DISTRIBUTED_ACCOUT_INFO");
68                 break;
69             }
70             case IN_DELETE_SELF: {
71                 DealWithFileDeleteEvent(fileName, id);
72                 break;
73             }
74             default: {
75                 ACCOUNT_LOGW("get event invalid!");
76                 return false;
77             }
78         }
79         return true;
80     };
81 }
82 #else
OhosAccountDataDeal(const std::string & configFileDir)83 OhosAccountDataDeal::OhosAccountDataDeal(const std::string &configFileDir)
84     : configFileDir_(configFileDir)
85 {
86     accountFileOperator_ = std::make_shared<AccountFileOperator>();
87     initOk_ = false;
88 }
89 #endif // ENABLE_FILE_WATCHER
90 
91 #ifdef ENABLE_FILE_WATCHER
DealWithFileModifyEvent(const std::string & fileName,const int32_t id)92 bool OhosAccountDataDeal::DealWithFileModifyEvent(const std::string &fileName, const int32_t id)
93 {
94     ACCOUNT_LOGI("enter");
95     {
96         std::unique_lock<std::shared_timed_mutex> lock(accountFileOperator_->fileLock_);
97         if (accountFileOperator_->GetValidModifyFileOperationFlag(fileName)) {
98             ACCOUNT_LOGD("this is valid service operate, no need to deal with it.");
99             accountFileOperator_->SetValidModifyFileOperationFlag(fileName, false);
100             return true;
101         }
102     }
103     std::lock_guard<std::mutex> lock(accountInfoFileLock_);
104     std::string fileInfoStr;
105     if (accountFileOperator_->GetFileContentByPath(fileName, fileInfoStr) != ERR_OK) {
106         ACCOUNT_LOGE("get content from file %{public}s failed!", fileName.c_str());
107         return false;
108     }
109     uint8_t localDigestData[ALG_COMMON_SIZE] = {0};
110     accountFileWatcherMgr_.GetAccountInfoDigestFromFile(fileName, localDigestData, ALG_COMMON_SIZE);
111 #ifdef HAS_HUKS_PART
112     uint8_t newDigestData[ALG_COMMON_SIZE] = {0};
113     GenerateAccountInfoDigest(fileInfoStr, newDigestData, ALG_COMMON_SIZE);
114     if (memcmp(localDigestData, newDigestData, ALG_COMMON_SIZE) == 0) {
115         ACCOUNT_LOGD("No need to recover local file data.");
116         return true;
117     }
118 #endif // HAS_HUKS_PART
119     ReportOsAccountDataTampered(id, fileName, "DISTRIBUTED_ACCOUT_INFO");
120     return true;
121 }
122 
DealWithFileDeleteEvent(const std::string & fileName,const int32_t id)123 void OhosAccountDataDeal::DealWithFileDeleteEvent(const std::string &fileName, const int32_t id)
124 {
125     {
126         std::unique_lock<std::shared_timed_mutex> lock(accountFileOperator_->fileLock_);
127         if (accountFileOperator_->GetValidDeleteFileOperationFlag(fileName)) {
128             ACCOUNT_LOGD("this is valid service operate, no need to deal with it.");
129             accountFileOperator_->SetValidDeleteFileOperationFlag(fileName, false);
130             accountFileWatcherMgr_.RemoveFileWatcher(id, fileName);
131             return;
132         }
133         std::string fileDir = configFileDir_ + std::to_string(id);
134         if (!accountFileOperator_->IsExistDir(fileDir)) {
135             ACCOUNT_LOGI("this id is already removed.");
136             return;
137         }
138     }
139     ReportOsAccountDataTampered(id, fileName, "DISTRIBUTED_ACCOUT_INFO");
140 }
141 
AddFileWatcher(const int32_t id)142 void OhosAccountDataDeal::AddFileWatcher(const int32_t id)
143 {
144     std::string configFile = configFileDir_ + std::to_string(id) + ACCOUNT_CFG_FILE_NAME;
145     accountFileWatcherMgr_.AddFileWatcher(id, checkCallbackFunc_, configFile);
146 }
147 #endif // ENABLE_FILE_WATCHER
148 
Init(int32_t userId)149 ErrCode OhosAccountDataDeal::Init(int32_t userId)
150 {
151     std::string configFile = configFileDir_ + std::to_string(userId) + ACCOUNT_CFG_FILE_NAME;
152     if (!accountFileOperator_->IsExistFile(configFile)) {
153         ACCOUNT_LOGI("file %{public}s not exist, create!", configFile.c_str());
154         BuildJsonFileFromScratch(userId);
155     }
156 
157     std::ifstream fin(configFile);
158     if (!fin) {
159         int32_t err = errno;
160         ACCOUNT_LOGE("Failed to open config file %{public}s, errno %{public}d.", configFile.c_str(), err);
161         ReportOhosAccountOperationFail(userId, OPERATION_INIT_OPEN_FILE_TO_READ, err, configFile);
162         return ERR_ACCOUNT_DATADEAL_INPUT_FILE_ERROR;
163     }
164 
165     // NOT-allow exceptions when parse json file
166     std::lock_guard<std::mutex> lock(mutex_);
167     std::string fileContent((std::istreambuf_iterator<char>(fin)), std::istreambuf_iterator<char>());
168     fin.close();
169     auto jsonData = CreateJsonFromString(fileContent);
170     if (jsonData == nullptr || !IsObject(jsonData)) {
171         ACCOUNT_LOGE("Invalid json file, remove");
172         if (RemoveFile(configFile)) {
173             int32_t err = errno;
174             ACCOUNT_LOGE("Remove invalid json file %{public}s failed, errno %{public}d.", configFile.c_str(), err);
175             ReportOhosAccountOperationFail(userId, OPERATION_REMOVE_FILE, err, configFile);
176         }
177         return ERR_ACCOUNT_DATADEAL_JSON_FILE_CORRUPTION;
178     }
179 
180     // recover watch for exist account info
181     std::vector<OsAccountInfo> osAccountInfos;
182     IInnerOsAccountManager::GetInstance().QueryAllCreatedOsAccounts(osAccountInfos);
183 #ifdef ENABLE_FILE_WATCHER
184     for (const auto &info : osAccountInfos) {
185         AddFileWatcher(info.GetLocalId());
186     }
187 #endif // ENABLE_FILE_WATCHER
188     initOk_ = true;
189     return ERR_OK;
190 }
191 
AccountInfoFromJson(AccountInfo & accountInfo,int32_t userId)192 ErrCode OhosAccountDataDeal::AccountInfoFromJson(AccountInfo &accountInfo, int32_t userId)
193 {
194     if (!initOk_) {
195         ACCOUNT_LOGE("not init yet!");
196         return ERR_ACCOUNT_DATADEAL_NOT_READY;
197     }
198     return GetAccountInfo(accountInfo, userId);
199 }
200 
AccountInfoToJson(const AccountInfo & accountInfo)201 ErrCode OhosAccountDataDeal::AccountInfoToJson(const AccountInfo &accountInfo)
202 {
203     if (!initOk_) {
204         ACCOUNT_LOGE("Not init ok");
205         return ERR_ACCOUNT_DATADEAL_NOT_READY;
206     }
207     return SaveAccountInfo(accountInfo);
208 }
209 
SaveAccountInfo(const AccountInfo & accountInfo)210 ErrCode OhosAccountDataDeal::SaveAccountInfo(const AccountInfo &accountInfo)
211 {
212     std::lock_guard<std::mutex> lock(accountInfoFileLock_);
213 
214     std::string scalableDataStr = (accountInfo.ohosAccountInfo_.scalableData_).ToString();
215     auto jsonData = CreateJson();
216     AddIntToJson(jsonData, DATADEAL_JSON_KEY_OHOSACCOUNT_VERSION, accountInfo.version_);
217     AddIntToJson(jsonData, DATADEAL_JSON_KEY_BIND_TIME, accountInfo.bindTime_);
218     AddIntToJson(jsonData, DATADEAL_JSON_KEY_USERID, accountInfo.userId_);
219     AddStringToJson(jsonData, DATADEAL_JSON_KEY_OHOSACCOUNT_NAME, accountInfo.ohosAccountInfo_.name_);
220     AddStringToJson(jsonData, DATADEAL_JSON_KEY_OHOSACCOUNT_RAW_UID, accountInfo.ohosAccountInfo_.GetRawUid());
221     AddStringToJson(jsonData, DATADEAL_JSON_KEY_OHOSACCOUNT_UID, accountInfo.ohosAccountInfo_.uid_);
222     AddIntToJson(jsonData, DATADEAL_JSON_KEY_OHOSACCOUNT_STATUS, accountInfo.ohosAccountInfo_.status_);
223     AddIntToJson(jsonData, DATADEAL_JSON_KEY_OHOSACCOUNT_CALLINGUID, accountInfo.ohosAccountInfo_.callingUid_);
224     AddStringToJson(jsonData, DATADEAL_JSON_KEY_OHOSACCOUNT_NICKNAME, accountInfo.ohosAccountInfo_.nickname_);
225     AddStringToJson(jsonData, DATADEAL_JSON_KEY_OHOSACCOUNT_SCALABLEDATA, scalableDataStr);
226 
227     std::string avatarFile = configFileDir_ + std::to_string(accountInfo.userId_) + ACCOUNT_AVATAR_NAME;
228     ErrCode ret = accountFileOperator_->InputFileByPathAndContentWithTransaction(
229         avatarFile, accountInfo.ohosAccountInfo_.avatar_);
230     if (ret != ERR_OK) {
231         ACCOUNT_LOGE("Failed to save avatar! ret = %{public}d", ret);
232         return ret;
233     }
234     std::string accountInfoValue = PackJsonToString(jsonData);
235     std::string configFile = configFileDir_ + std::to_string(accountInfo.userId_) + ACCOUNT_CFG_FILE_NAME;
236 
237     ret = accountFileOperator_->InputFileByPathAndContent(configFile, accountInfoValue);
238     if (ret == ERR_OHOSACCOUNT_SERVICE_FILE_CHANGE_DIR_MODE_ERROR) {
239         ReportOhosAccountOperationFail(accountInfo.userId_, OPERATION_CHANGE_MODE_FILE, ret, configFile);
240     }
241     if (ret != ERR_OK && ret != ERR_OHOSACCOUNT_SERVICE_FILE_CHANGE_DIR_MODE_ERROR) {
242         ReportOhosAccountOperationFail(accountInfo.userId_, OPERATION_OPEN_FILE_TO_WRITE, ret, configFile);
243     }
244 
245     // report data_size when distributed account profile photo updated
246     std::vector<int32_t> currentId{accountInfo.userId_};
247     ReportUserDataSize(currentId);
248 #ifdef ENABLE_FILE_WATCHER
249     accountFileWatcherMgr_.AddAccountInfoDigest(accountInfoValue, configFile);
250 #endif // ENABLE_FILE_WATCHER
251     return ret;
252 }
253 
ParseJsonFromFile(const std::string & filePath,CJsonUnique & jsonData,int32_t userId)254 ErrCode OhosAccountDataDeal::ParseJsonFromFile(const std::string &filePath, CJsonUnique &jsonData, int32_t userId)
255 {
256     std::ifstream fin(filePath);
257     if (!fin) {
258         int32_t err = errno;
259         ACCOUNT_LOGE("Failed to open config file %{public}s, errno %{public}d.", filePath.c_str(), err);
260         ReportOhosAccountOperationFail(userId, OPERATION_OPEN_FILE_TO_READ, err, filePath);
261         return ERR_ACCOUNT_DATADEAL_INPUT_FILE_ERROR;
262     }
263     std::string fileContent((std::istreambuf_iterator<char>(fin)), std::istreambuf_iterator<char>());
264     fin.close();
265     jsonData = CreateJsonFromString(fileContent);
266     if (jsonData == nullptr || !IsObject(jsonData)) {
267         ACCOUNT_LOGE("Invalid json file,  %{public}s, remove", filePath.c_str());
268         return ERR_ACCOUNT_DATADEAL_JSON_FILE_CORRUPTION;
269     }
270     std::string avatarData;
271     if (IsKeyExist(jsonData, DATADEAL_JSON_KEY_OHOSACCOUNT_AVATAR)) {
272         cJSON *it = GetItemFromJson(jsonData, DATADEAL_JSON_KEY_OHOSACCOUNT_AVATAR);
273         if (IsString(it)) {
274             AddStringToJson(jsonData, DATADEAL_JSON_KEY_OHOSACCOUNT_AVATAR,  it->valuestring);
275         }
276     } else {
277         std::string avatarFile = configFileDir_ + std::to_string(userId) + ACCOUNT_AVATAR_NAME;
278         if (accountFileOperator_->GetFileContentByPath(avatarFile, avatarData) == ERR_OK) {
279             AddStringToJson(jsonData, DATADEAL_JSON_KEY_OHOSACCOUNT_AVATAR,  avatarData);
280         }
281     }
282     return ERR_OK;
283 }
284 
285 template <typename T, typename Callback>
GetJsonField(CJsonUnique & jsonData,const std::string & key,Callback callback)286 bool GetJsonField(CJsonUnique &jsonData, const std::string &key, Callback callback)
287 {
288     if (!IsKeyExist(jsonData, key)) {
289         return false;
290     }
291     auto it = GetItemFromJson(jsonData, key);
292     if constexpr (std::is_same_v<T, int> || std::is_same_v<T, std::time_t>) {
293         if (!IsNumber(it)) {
294             return false;
295         }
296         T value = static_cast<T>(GetJsonNumberValue(it));
297         callback(value);
298         return true;
299     }
300     if constexpr (std::is_same_v<T, std::string>) {
301         if (!IsString(it)) {
302             return false;
303         }
304         T value = it->valuestring;
305         callback(value);
306         return true;
307     }
308     return false;
309 }
310 
GetAccountInfoFromJson(CJsonUnique & jsonData,AccountInfo & accountInfo,const int32_t userId)311 ErrCode OhosAccountDataDeal::GetAccountInfoFromJson(
312     CJsonUnique &jsonData, AccountInfo &accountInfo, const int32_t userId)
313 {
314     GetJsonField<int>(jsonData, DATADEAL_JSON_KEY_OHOSACCOUNT_VERSION, [&](int value) {
315         accountInfo.version_ = value;
316     });
317 
318     GetJsonField<std::time_t>(jsonData, DATADEAL_JSON_KEY_BIND_TIME, [&](std::time_t value) {
319         accountInfo.bindTime_ = value;
320     });
321 
322     GetJsonField<std::string>(jsonData, DATADEAL_JSON_KEY_OHOSACCOUNT_NAME, [&](const std::string &value) {
323         accountInfo.ohosAccountInfo_.name_ = value;
324     });
325 
326     GetJsonField<std::string>(jsonData, DATADEAL_JSON_KEY_OHOSACCOUNT_RAW_UID, [&](const std::string &value) {
327         accountInfo.ohosAccountInfo_.SetRawUid(value);
328     });
329 
330     GetJsonField<std::string>(jsonData, DATADEAL_JSON_KEY_OHOSACCOUNT_UID, [&](const std::string &value) {
331         accountInfo.ohosAccountInfo_.uid_ = value;
332     });
333 
334     GetJsonField<int>(jsonData, DATADEAL_JSON_KEY_OHOSACCOUNT_STATUS, [&](int value) {
335         accountInfo.ohosAccountInfo_.status_ = value;
336     });
337 
338     GetJsonField<int>(jsonData, DATADEAL_JSON_KEY_OHOSACCOUNT_CALLINGUID, [&](int value) {
339         accountInfo.ohosAccountInfo_.callingUid_ = value;
340     });
341 
342     GetJsonField<std::string>(jsonData, DATADEAL_JSON_KEY_OHOSACCOUNT_NICKNAME, [&](const std::string &value) {
343         accountInfo.ohosAccountInfo_.nickname_ = value;
344     });
345 
346     GetJsonField<std::string>(jsonData, DATADEAL_JSON_KEY_OHOSACCOUNT_AVATAR, [&](const std::string &value) {
347         accountInfo.ohosAccountInfo_.avatar_ = value;
348     });
349 
350     GetJsonField<std::string>(jsonData, DATADEAL_JSON_KEY_OHOSACCOUNT_SCALABLEDATA, [&](std::string &value) {
351         sptr<AAFwk::Want> want = AAFwk::Want::FromString(value);
352         if (want != nullptr) {
353             accountInfo.ohosAccountInfo_.scalableData_ = *want;
354         }
355     });
356 
357     accountInfo.userId_ = userId;
358     return ERR_OK;
359 }
360 
GetAccountInfo(AccountInfo & accountInfo,const int32_t userId)361 ErrCode OhosAccountDataDeal::GetAccountInfo(AccountInfo &accountInfo, const int32_t userId)
362 {
363     if (userId < 0) {
364         ACCOUNT_LOGW("invalid userid = %{public}d", userId);
365         return ERR_OSACCOUNT_SERVICE_MANAGER_ID_ERROR;
366     }
367     std::string configFile = configFileDir_ + std::to_string(userId) + ACCOUNT_CFG_FILE_NAME;
368     ErrCode ret = accountFileOperator_->CheckFileExistence(configFile);
369     if (ret != ERR_OK) {
370         if (ret != ERR_ACCOUNT_COMMON_FILE_NOT_EXIST) {
371             std::string errorMsg = "Stat " + configFile + " failed";
372             ReportOhosAccountOperationFail(userId, OPERATION_OPEN_FILE_TO_READ, ret, errorMsg);
373             return ERR_ACCOUNT_DATADEAL_INPUT_FILE_ERROR;
374         } else {
375             ACCOUNT_LOGI("File %{public}s not exist, create!", configFile.c_str());
376             BuildJsonFileFromScratch(userId); // create default config file for first login
377         }
378     }
379     std::lock_guard<std::mutex> lock(mutex_);
380     auto jsonData = CreateJson();
381     ret = ParseJsonFromFile(configFile, jsonData, userId);
382     if (ret != ERR_OK) {
383         return ret;
384     }
385     return GetAccountInfoFromJson(jsonData, accountInfo, userId);
386 }
387 
BuildJsonFileFromScratch(int32_t userId)388 void OhosAccountDataDeal::BuildJsonFileFromScratch(int32_t userId)
389 {
390     AccountInfo accountInfo;
391     accountInfo.userId_ = userId;
392     accountInfo.bindTime_ = 0;
393     accountInfo.ohosAccountInfo_.uid_ = DEFAULT_OHOS_ACCOUNT_UID;
394     accountInfo.ohosAccountInfo_.name_ = DEFAULT_OHOS_ACCOUNT_NAME;
395     accountInfo.ohosAccountInfo_.status_ = ACCOUNT_STATE_UNBOUND;
396     accountInfo.ohosAccountInfo_.callingUid_ = DEFAULT_CALLING_UID;
397     accountInfo.digest_ = "";
398     accountInfo.ohosAccountInfo_.SetRawUid(DEFAULT_OHOS_ACCOUNT_UID);
399     SaveAccountInfo(accountInfo);
400 #ifdef ENABLE_FILE_WATCHER
401     AddFileWatcher(userId);
402 #endif // ENABLE_FILE_WATCHER
403 }
404 } // namespace AccountSA
405 } // namespace OHOS