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