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