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