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