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