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