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