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