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