1 /*
2 * Copyright (c) 2023-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
16 #include "account_file_watcher_manager.h"
17
18 #include <dlfcn.h>
19 #include <pthread.h>
20 #include <securec.h>
21 #include <thread>
22 #include "account_hisysevent_adapter.h"
23 #include "account_log_wrapper.h"
24 #include "account_timeout_task.h"
25 #ifdef HAS_HUKS_PART
26 #include "hks_api.h"
27 #include "hks_param.h"
28 #include "hks_type.h"
29 #endif // HAS_HUKS_PART
30 #include "os_account_constants.h"
31 #include "hitrace_adapter.h"
32
33 namespace OHOS {
34 namespace AccountSA {
35 namespace {
36 constexpr uint32_t FILE_WATCHER_LIMIT = 1024 * 100;
37 constexpr uint32_t BUF_COMMON_SIZE = 1024 * 100;
38 constexpr uint32_t ALG_COMMON_SIZE = 32;
39 #ifdef HAS_HUKS_PART
40 const struct HksParam g_genSignVerifyParams[] = {
41 {
42 .tag = HKS_TAG_ALGORITHM,
43 .uint32Param = HKS_ALG_HMAC
44 }, {
45 .tag = HKS_TAG_PURPOSE,
46 .uint32Param = HKS_KEY_PURPOSE_MAC
47 }, {
48 .tag = HKS_TAG_KEY_SIZE,
49 .uint32Param = HKS_AES_KEY_SIZE_256
50 }, {
51 .tag = HKS_TAG_DIGEST,
52 .uint32Param = HKS_DIGEST_SHA256
53 }, {
54 .tag = HKS_TAG_AUTH_STORAGE_LEVEL,
55 .uint32Param = HKS_AUTH_STORAGE_LEVEL_DE
56 }
57 };
58 constexpr int32_t TIMES = 4;
59 constexpr int32_t MAX_UPDATE_SIZE = 256 * 100;
60 constexpr int32_t MAX_OUTDATA_SIZE = MAX_UPDATE_SIZE * TIMES;
61 constexpr char ACCOUNT_KEY_ALIAS[] = "os_account_info_encryption_key";
62 const HksBlob g_keyAlias = { (uint32_t)strlen(ACCOUNT_KEY_ALIAS), (uint8_t *)ACCOUNT_KEY_ALIAS };
63 #endif // HAS_HUKS_PART
64 }
65
66 #ifdef HAS_HUKS_PART
InitParamSet(struct HksParamSet ** paramSet,const struct HksParam * params,uint32_t paramCount)67 static int32_t InitParamSet(struct HksParamSet **paramSet, const struct HksParam *params, uint32_t paramCount)
68 {
69 int32_t ret = HksInitParamSet(paramSet);
70 if (ret != 0) {
71 ACCOUNT_LOGE("HksInitParamSet err = %{public}d", ret);
72 return ret;
73 }
74 ret = HksAddParams(*paramSet, params, paramCount);
75 if (ret != 0) {
76 ACCOUNT_LOGE("HksAddParams err = %{public}d", ret);
77 HksFreeParamSet(paramSet);
78 return ret;
79 }
80
81 ret = HksBuildParamSet(paramSet);
82 if (ret != 0) {
83 ACCOUNT_LOGE("HksBuildParamSet err = %{public}d", ret);
84 HksFreeParamSet(paramSet);
85 return ret;
86 }
87 return ret;
88 }
89
MallocAndCheckBlobData(struct HksBlob * blob,const uint32_t blobSize)90 static int32_t MallocAndCheckBlobData(struct HksBlob *blob, const uint32_t blobSize)
91 {
92 if (blobSize == 0) {
93 blob->data = NULL;
94 return -1;
95 }
96 blob->data = static_cast<uint8_t *>(malloc(blobSize));
97 if (blob->data == NULL) {
98 ACCOUNT_LOGE("MallocAndCheckBlobData err");
99 return -1;
100 }
101 return 0;
102 }
103
HksUpdateOpt(const struct HksBlob * handle,const struct HksParamSet * paramSet,const struct HksBlob * inData)104 static int32_t HksUpdateOpt(
105 const struct HksBlob *handle, const struct HksParamSet *paramSet, const struct HksBlob *inData)
106 {
107 struct HksBlob inDataSeg = *inData;
108 inDataSeg.size = MAX_UPDATE_SIZE;
109
110 uint8_t *lastPtr = inData->data + inData->size - 1;
111 struct HksBlob outDataSeg = {
112 .size = MAX_OUTDATA_SIZE,
113 .data = NULL
114 };
115
116 bool isFinished = false;
117 while (inDataSeg.data <= lastPtr) {
118 if (inDataSeg.data + MAX_UPDATE_SIZE <= lastPtr) {
119 outDataSeg.size = MAX_OUTDATA_SIZE;
120 } else {
121 isFinished = true;
122 inDataSeg.size = lastPtr - inDataSeg.data + 1;
123 outDataSeg.size = inDataSeg.size + MAX_UPDATE_SIZE;
124 }
125 if (MallocAndCheckBlobData(&outDataSeg, outDataSeg.size) != 0) {
126 return -1;
127 }
128 int32_t ret = HksUpdate(handle, paramSet, &inDataSeg, &outDataSeg);
129 if (ret != 0) {
130 ACCOUNT_LOGE("HksUpdate err, ret = %{public}d", ret);
131 free(outDataSeg.data);
132 outDataSeg.data = NULL;
133 return -1;
134 }
135 free(outDataSeg.data);
136 outDataSeg.data = NULL;
137 if ((isFinished == false) && (inDataSeg.data + MAX_UPDATE_SIZE > lastPtr)) {
138 return 0;
139 }
140 inDataSeg.data += MAX_UPDATE_SIZE;
141 }
142 return 0;
143 }
144
InitEncryptionKey()145 static int32_t InitEncryptionKey()
146 {
147 struct HksParamSet *genParamSet = nullptr;
148
149 int32_t ret;
150 do {
151 ret = InitParamSet(&genParamSet, g_genSignVerifyParams, sizeof(g_genSignVerifyParams) / sizeof(HksParam));
152 if (ret != 0) {
153 ACCOUNT_LOGE("InitParamSet genParamSet err = %{public}d", ret);
154 break;
155 }
156 ret = HksGenerateKey(&g_keyAlias, genParamSet, nullptr);
157 if (ret != 0) {
158 ACCOUNT_LOGE("HksGenerateKey err = %{public}d", ret);
159 break;
160 }
161 } while (0);
162 HksFreeParamSet(&genParamSet);
163 return ret;
164 }
165
GetDigestDataFromHuks(struct HksParamSet * genParamSet,struct HksBlob & inDataBlob,uint8_t * outData,uint32_t size)166 static int32_t GetDigestDataFromHuks(struct HksParamSet *genParamSet, struct HksBlob &inDataBlob,
167 uint8_t* outData, uint32_t size)
168 {
169 uint8_t handleTmp[sizeof(uint64_t)] = {0};
170 struct HksBlob handleGenDigest = { (uint32_t)sizeof(uint64_t), handleTmp };
171
172 int32_t ret = HksInit(&g_keyAlias, genParamSet, &handleGenDigest, nullptr);
173 if (ret != 0) {
174 ACCOUNT_LOGE("HksInit err = %{public}d", ret);
175 return ret;
176 }
177 ret = HksUpdateOpt(&handleGenDigest, genParamSet, &inDataBlob);
178 if (ret != 0) {
179 ACCOUNT_LOGE("HksUpdateOpt err = %{public}d", ret);
180 HksAbort(&handleGenDigest, genParamSet);
181 return ret;
182 }
183 struct HksBlob finishOut = { 0, nullptr };
184 uint8_t outDataS[ALG_COMMON_SIZE] = "out";
185 struct HksBlob outDataBlob = { ALG_COMMON_SIZE, outDataS };
186 ret = HksFinish(&handleGenDigest, genParamSet, &finishOut, &outDataBlob);
187 if (ret != 0) {
188 ACCOUNT_LOGE("HksFinish failed = %{public}d", ret);
189 HksAbort(&handleGenDigest, genParamSet);
190 return ret;
191 }
192 if (memcpy_s(outData, size, outDataS, outDataBlob.size) != EOK) {
193 ACCOUNT_LOGE("Get digest failed duo to memcpy_s failed");
194 return -1;
195 }
196 return 0;
197 }
198
GenerateAccountInfoDigest(const std::string & inData,uint8_t * outData,uint32_t size)199 int32_t GenerateAccountInfoDigest(const std::string &inData, uint8_t* outData, uint32_t size)
200 {
201 if (inData.empty()) {
202 ACCOUNT_LOGW("inData is empty.");
203 return 0;
204 }
205 size_t len = inData.size() + 1;
206 uint8_t *buffer = static_cast<uint8_t *>(malloc(len));
207 if (buffer == nullptr) {
208 ACCOUNT_LOGE("buffer malloc err");
209 return -1;
210 }
211 (void)memcpy_s(buffer, len, inData.c_str(), len);
212 struct HksBlob inDataBlob = { inData.size(), buffer };
213 struct HksParamSet *genParamSet = nullptr;
214 int32_t ret = InitParamSet(&genParamSet, g_genSignVerifyParams, sizeof(g_genSignVerifyParams) / sizeof(HksParam));
215 if (ret != 0) {
216 free(buffer);
217 ACCOUNT_LOGE("InitParamSet err = %{public}d", ret);
218 return ret;
219 }
220 ret = GetDigestDataFromHuks(genParamSet, inDataBlob, outData, size);
221 HksFreeParamSet(&genParamSet);
222 free(buffer);
223 return ret;
224 }
225 #endif // HAS_HUKS_PART
226
AccountFileWatcherMgr()227 AccountFileWatcherMgr::AccountFileWatcherMgr()
228 {
229 std::shared_ptr<AccountTimeoutTask> task = std::make_shared<AccountTimeoutTask>();
230 bool state = task->RunTask("InitEncryptionKey", [] {
231 #ifdef HAS_HUKS_PART
232 InitEncryptionKey();
233 #endif // HAS_HUKS_PART
234 });
235 if (!state) {
236 ReportServiceStartFail(ERR_ACCOUNT_COMMON_OPERATION_TIMEOUT, "InitEncryptionKey timeout");
237 }
238 inotifyFd_ = inotify_init();
239 if (inotifyFd_ < 0) {
240 ACCOUNT_LOGE("failed to init notify, errCode:%{public}d", errno);
241 }
242 accountFileOperator_ = std::make_shared<AccountFileOperator>();
243 FD_ZERO(&fds_);
244 }
245
GetInstance()246 AccountFileWatcherMgr &AccountFileWatcherMgr::GetInstance()
247 {
248 static AccountFileWatcherMgr *instance = new AccountFileWatcherMgr();
249 return *instance;
250 }
251
DealWithFileEvent()252 void AccountFileWatcherMgr::DealWithFileEvent()
253 {
254 std::vector<std::pair<std::shared_ptr<FileWatcher>, uint32_t>> eventMap;
255 {
256 std::lock_guard<std::mutex> lock(fileWatcherMgrLock_);
257 char buf[BUF_COMMON_SIZE] = {0};
258 struct inotify_event *event = nullptr;
259 int len = 0;
260 int index = 0;
261 while (((len = read(inotifyFd_, &buf, sizeof(buf))) < 0) && (errno == EINTR)) {};
262 while (index < len) {
263 event = reinterpret_cast<inotify_event *>(buf + index);
264 if (event->mask & (IN_MODIFY | IN_DELETE_SELF | IN_MOVE_SELF)) {
265 if (fileNameMgrMap_.find(event->wd) != fileNameMgrMap_.end()) {
266 std::shared_ptr<FileWatcher> fileWatcher = fileNameMgrMap_[event->wd];
267 eventMap.emplace_back(std::make_pair(fileWatcher, event->mask));
268 }
269 }
270 index += static_cast<int>(sizeof(struct inotify_event) + event->len);
271 }
272 }
273 for (auto it : eventMap) {
274 it.first->CheckNotifyEvent(it.second);
275 }
276 }
277
GetNotifyEvent()278 void AccountFileWatcherMgr::GetNotifyEvent()
279 {
280 FD_SET(inotifyFd_, &fds_);
281 while (run_) {
282 if (inotifyFd_ < 0) {
283 ACCOUNT_LOGE("failed to run notify because no fd available.");
284 break;
285 }
286 if (select(inotifyFd_ + 1, &fds_, nullptr, nullptr, nullptr) <= 0) {
287 continue;
288 }
289 DealWithFileEvent();
290 }
291 }
292
StartWatch()293 void AccountFileWatcherMgr::StartWatch() // start watcher
294 {
295 if (run_) {
296 return;
297 }
298 run_ = true;
299 auto task = [this] { this->GetNotifyEvent(); };
300 std::thread taskThread(task);
301 pthread_setname_np(taskThread.native_handle(), "fileWatcher");
302 taskThread.detach();
303 }
304
AddFileWatcher(int32_t id,CheckNotifyEventCallbackFunc checkCallbackFunc,const std::string & filePath)305 void AccountFileWatcherMgr::AddFileWatcher(
306 int32_t id, CheckNotifyEventCallbackFunc checkCallbackFunc, const std::string &filePath)
307 {
308 if (checkCallbackFunc == nullptr) {
309 ACCOUNT_LOGE("Notify event callback is nullptr");
310 return;
311 }
312 std::lock_guard<std::mutex> lock(fileWatcherMgrLock_);
313 if (inotifyFd_ < 0) {
314 inotifyFd_ = inotify_init();
315 if (inotifyFd_ < 0) {
316 ACCOUNT_LOGE("failed to init notify, errCode:%{public}d", errno);
317 return;
318 }
319 }
320 if (fileNameMgrMap_.size() > FILE_WATCHER_LIMIT) {
321 ACCOUNT_LOGW("the fileWatcher limit has been reached, fileName = %{public}s", filePath.c_str());
322 return;
323 }
324 std::shared_ptr<FileWatcher> fileWatcher;
325 if (!filePath.empty()) {
326 fileWatcher = std::make_shared<FileWatcher>(id, filePath, checkCallbackFunc);
327 } else {
328 fileWatcher = std::make_shared<FileWatcher>(id, checkCallbackFunc);
329 }
330 if (!fileWatcher->StartNotify(inotifyFd_, IN_MODIFY | IN_DELETE_SELF| IN_MOVE_SELF)) {
331 ACCOUNT_LOGI("fileWatcher StartNotify failed, fileName = %{public}s", filePath.c_str());
332 return;
333 }
334 fileNameMgrMap_[fileWatcher->GetWd()] = fileWatcher;
335 {
336 std::unique_lock<std::shared_timed_mutex> fileLock(accountFileOperator_->fileLock_);
337 accountFileOperator_->SetValidModifyFileOperationFlag(filePath, false);
338 }
339
340 StartWatch();
341 }
342
RemoveFileWatcher(int32_t id,const std::string & filePath)343 void AccountFileWatcherMgr::RemoveFileWatcher(int32_t id, const std::string &filePath)
344 {
345 std::lock_guard<std::mutex> lock(fileWatcherMgrLock_);
346 int targetWd = -1;
347 for (auto it : fileNameMgrMap_) {
348 if ((it.second->GetLocalId() == id) && (it.second->GetFilePath() == filePath)) {
349 targetWd = it.second->GetWd();
350 break;
351 }
352 }
353 if (targetWd == -1) {
354 return;
355 }
356 fileNameMgrMap_[targetWd]->CloseNotify(inotifyFd_);
357 fileNameMgrMap_.erase(targetWd);
358 }
359
GetAccountInfoDigestFromFile(const std::string & path,uint8_t * digest,uint32_t size)360 ErrCode AccountFileWatcherMgr::GetAccountInfoDigestFromFile(const std::string &path, uint8_t *digest, uint32_t size)
361 {
362 std::string accountInfoDigest;
363 std::lock_guard<std::mutex> lock(accountInfoDigestFileLock_);
364 ErrCode errCode = accountFileOperator_->GetFileContentByPath(Constants::ACCOUNT_INFO_DIGEST_FILE_PATH,
365 accountInfoDigest);
366 if (errCode != ERR_OK) {
367 ACCOUNT_LOGE("GetFileContentByPath failed! error code %{public}d.", errCode);
368 return errCode;
369 }
370 Json accountInfoDigestJson = Json::parse(accountInfoDigest, nullptr, false);
371 if (accountInfoDigestJson.is_discarded()) {
372 return ERR_ACCOUNT_COMMON_DUMP_JSON_ERROR;
373 }
374 std::vector<uint8_t> digestTmp;
375 OHOS::AccountSA::GetDataByType<std::vector<uint8_t>>(accountInfoDigestJson,
376 accountInfoDigestJson.end(), path, digestTmp, OHOS::AccountSA::JsonType::ARRAY);
377 if (memcpy_s(digest, size, digestTmp.data(), ALG_COMMON_SIZE) != EOK) {
378 ACCOUNT_LOGE("Get digest failed duo to memcpy_s failed");
379 return ERR_ACCOUNT_COMMON_INSUFFICIENT_MEMORY_ERROR;
380 }
381 return ERR_OK;
382 }
383
GenerateAccountInfoDigestStr(const std::string & userInfoPath,const std::string & accountInfoStr,std::string & digestStr)384 ErrCode AccountFileWatcherMgr::GenerateAccountInfoDigestStr(
385 const std::string &userInfoPath, const std::string &accountInfoStr, std::string &digestStr)
386 {
387 uint8_t digestOutData[ALG_COMMON_SIZE];
388 #ifdef HAS_HUKS_PART
389 StartTraceAdapter("GenerateAccountInfoDigest Using Huks");
390 GenerateAccountInfoDigest(accountInfoStr, digestOutData, ALG_COMMON_SIZE);
391 FinishTraceAdapter();
392 #endif // HAS_HUKS_PART
393
394 std::string accountInfoDigest;
395 std::lock_guard<std::mutex> lock(accountInfoDigestFileLock_);
396 ErrCode errCode = accountFileOperator_->GetFileContentByPath(Constants::ACCOUNT_INFO_DIGEST_FILE_PATH,
397 accountInfoDigest);
398 if (errCode != ERR_OK) {
399 ACCOUNT_LOGE("get file content failed! error code %{public}d.", errCode);
400 return errCode;
401 }
402 Json accountInfoDigestJson = Json::parse(accountInfoDigest, nullptr, false);
403 if (accountInfoDigestJson.is_discarded()) {
404 return ERR_ACCOUNT_COMMON_DUMP_JSON_ERROR;
405 }
406 accountInfoDigestJson[userInfoPath] = digestOutData;
407 try {
408 digestStr = accountInfoDigestJson.dump();
409 } catch (Json::type_error& err) {
410 ACCOUNT_LOGE("failed to dump json object, reason: %{public}s", err.what());
411 return ERR_ACCOUNT_COMMON_DUMP_JSON_ERROR;
412 }
413 return ERR_OK;
414 }
415
AddAccountInfoDigest(const std::string accountInfo,const std::string & userInfoPath)416 ErrCode AccountFileWatcherMgr::AddAccountInfoDigest(const std::string accountInfo, const std::string &userInfoPath)
417 {
418 std::string digestStr;
419 if (GenerateAccountInfoDigestStr(userInfoPath, accountInfo, digestStr) == ERR_OK) {
420 std::lock_guard<std::mutex> lock(accountInfoDigestFileLock_);
421 return accountFileOperator_->InputFileByPathAndContent(Constants::ACCOUNT_INFO_DIGEST_FILE_PATH, digestStr);
422 }
423 return ERR_OK;
424 }
425
DeleteAccountInfoDigest(const std::string & userInfoPath)426 ErrCode AccountFileWatcherMgr::DeleteAccountInfoDigest(const std::string &userInfoPath)
427 {
428 std::string accountInfoDigest;
429 std::lock_guard<std::mutex> lock(accountInfoDigestFileLock_);
430 ErrCode errCode = accountFileOperator_->GetFileContentByPath(Constants::ACCOUNT_INFO_DIGEST_FILE_PATH,
431 accountInfoDigest);
432 if (errCode != ERR_OK) {
433 ACCOUNT_LOGE("get file content failed! error code %{public}d.", errCode);
434 return errCode;
435 }
436 Json accountInfoDigestJson = Json::parse(accountInfoDigest, nullptr, false);
437 if (accountInfoDigestJson.is_discarded()) {
438 return ERR_ACCOUNT_COMMON_DUMP_JSON_ERROR;
439 }
440 if (accountInfoDigestJson.find(userInfoPath) == accountInfoDigestJson.end()) {
441 return ERR_OK;
442 }
443 accountInfoDigestJson.erase(userInfoPath);
444
445 ErrCode result = accountFileOperator_->InputFileByPathAndContent(
446 Constants::ACCOUNT_INFO_DIGEST_FILE_PATH, accountInfoDigestJson.dump());
447 if (result != ERR_OK) {
448 ACCOUNT_LOGE("cannot save digest info to file, code %{public}d.", result);
449 return result;
450 }
451 return ERR_OK;
452 }
453
FileWatcher(int32_t id,const CheckNotifyEventCallbackFunc & checkCallbackFunc)454 FileWatcher::FileWatcher(int32_t id, const CheckNotifyEventCallbackFunc &checkCallbackFunc)
455 : id_(id), eventCallbackFunc_(checkCallbackFunc)
456 {
457 filePath_ = Constants::USER_INFO_BASE + Constants::PATH_SEPARATOR + std::to_string(id) +
458 Constants::PATH_SEPARATOR + Constants::USER_INFO_FILE_NAME;
459 }
460
FileWatcher(int32_t id,const std::string & filePath,const CheckNotifyEventCallbackFunc & checkCallbackFunc)461 FileWatcher::FileWatcher(int32_t id, const std::string &filePath,
462 const CheckNotifyEventCallbackFunc &checkCallbackFunc)
463 : id_(id), filePath_(filePath), eventCallbackFunc_(checkCallbackFunc)
464 {}
465
~FileWatcher()466 FileWatcher::~FileWatcher()
467 {}
468
GetFilePath() const469 std::string FileWatcher::GetFilePath() const
470 {
471 return filePath_;
472 }
473
StartNotify(int32_t fd,const uint32_t & watchEvents)474 bool FileWatcher::StartNotify(int32_t fd, const uint32_t &watchEvents)
475 {
476 wd_ = inotify_add_watch(fd, filePath_.c_str(), watchEvents);
477 if (wd_ < 0) {
478 ACCOUNT_LOGE("failed to start notify, errCode:%{public}d", errno);
479 return false;
480 }
481 return true;
482 }
483
CheckNotifyEvent(uint32_t event)484 bool FileWatcher::CheckNotifyEvent(uint32_t event)
485 {
486 if (eventCallbackFunc_ == nullptr) {
487 ACCOUNT_LOGW("eventCallbackFunc_ is nullptr.");
488 return false;
489 }
490 if (!eventCallbackFunc_(filePath_, id_, event)) {
491 ACCOUNT_LOGW("deal notify event failed.");
492 return false;
493 }
494 return true;
495 }
496
GetLocalId() const497 int32_t FileWatcher::GetLocalId() const
498 {
499 return id_;
500 }
501
GetWd() const502 int32_t FileWatcher::GetWd() const
503 {
504 return wd_;
505 }
506
CloseNotify(int32_t fd)507 void FileWatcher::CloseNotify(int32_t fd)
508 {
509 if (inotify_rm_watch(fd, wd_) == -1) {
510 ACCOUNT_LOGE("failed to remove wd, err:%{public}d", errno);
511 if (access(filePath_.c_str(), F_OK) == 0) {
512 ACCOUNT_LOGE("file already exist");
513 return;
514 }
515 }
516 wd_ = -1;
517 }
518 } // namespace AccountSA
519 } // namespace OHOS