1 /*
2  * Copyright (c) 2023 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 #include "first_use_dialog.h"
16 
17 #include <fcntl.h>
18 #include <fstream>
19 #include <sys/stat.h>
20 #include <sys/types.h>
21 #include <unistd.h>
22 #include "ability_manager_client.h"
23 #include "accesstoken_kit.h"
24 #include "bundle_mgr_client.h"
25 #include "hisysevent.h"
26 #include "ipc_skeleton.h"
27 #include "sec_comp_dialog_callback_proxy.h"
28 #include "sec_comp_err.h"
29 #include "sec_comp_log.h"
30 #include "want_params_wrapper.h"
31 #include "want.h"
32 
33 namespace OHOS {
34 namespace Security {
35 namespace SecurityComponent {
36 namespace {
37 constexpr OHOS::HiviewDFX::HiLogLabel LABEL = {LOG_CORE, SECURITY_DOMAIN_SECURITY_COMPONENT, "FirstUseDialog"};
38 static const std::string SEC_COMP_SRV_CFG_PATH = "/data/service/el1/public/security_component_service";
39 static const std::string FIRST_USE_RECORD_JSON = SEC_COMP_SRV_CFG_PATH + "/first_use_record.json";
40 static const std::string FIRST_USE_RECORD_TAG = "FirstUseRecord";
41 static const std::string TOKEN_ID_TAG = "TokenId";
42 static const std::string COMP_TYPE_TAG = "CompType";
43 
44 const std::string GRANT_ABILITY_BUNDLE_NAME = "com.ohos.permissionmanager";
45 const std::string GRANT_ABILITY_ABILITY_NAME = "com.ohos.permissionmanager.SecurityExtAbility";
46 const std::string TYPE_KEY = "ohos.user.security.type";
47 const std::string TOKEN_KEY = "ohos.ability.params.token";
48 const std::string CALLBACK_KEY = "ohos.ability.params.callback";
49 const std::string CALLER_UID_KEY = "ohos.caller.uid";
50 
51 constexpr uint32_t MAX_CFG_FILE_SIZE = 100 * 1024; // 100k
52 constexpr uint64_t LOCATION_BUTTON_FIRST_USE = 1 << 0;
53 constexpr uint64_t SAVE_BUTTON_FIRST_USE = 1 << 1;
54 }
55 
OnDialogClosed(int32_t result)56 void SecCompDialogSrvCallback::OnDialogClosed(int32_t result)
57 {
58     SC_LOG_INFO(LABEL, "Call dialog close callback scId_ %{public}d", scId_);
59     int32_t grantRes = FirstUseDialog::GetInstance().GrantDialogWaitEntity(scId_);
60     if (grantRes == SC_SERVICE_ERROR_COMPONENT_NOT_EXIST) {
61         SC_LOG_ERROR(LABEL, "Call dialog close callback scId_ %{public}d is not exist", scId_);
62         return;
63     }
64     if (!grantRes && !FirstUseDialog::GetInstance().SetFirstUseMap(sc_)) {
65         return;
66     }
67     auto callback = iface_cast<ISecCompDialogCallback>(dialogCallback_);
68     if (callback != nullptr) {
69         callback->OnDialogClosed(grantRes);
70     }
71 }
72 
GetInstance()73 FirstUseDialog& FirstUseDialog::GetInstance()
74 {
75     static FirstUseDialog instance;
76     return instance;
77 }
78 
IsCfgDirExist(void)79 bool FirstUseDialog::IsCfgDirExist(void)
80 {
81     struct stat fstat = {};
82     if (stat(SEC_COMP_SRV_CFG_PATH.c_str(), &fstat) != 0) {
83         SC_LOG_INFO(LABEL, "path %{public}s errno %{public}d.", SEC_COMP_SRV_CFG_PATH.c_str(), errno);
84         return false;
85     }
86 
87     if (!S_ISDIR(fstat.st_mode)) {
88         SC_LOG_ERROR(LABEL, "path %{public}s is not directory.", SEC_COMP_SRV_CFG_PATH.c_str());
89         return false;
90     }
91     return true;
92 }
93 
IsCfgFileExist(void)94 bool FirstUseDialog::IsCfgFileExist(void)
95 {
96     struct stat fstat = {};
97     if (stat(FIRST_USE_RECORD_JSON.c_str(), &fstat) != 0) {
98         SC_LOG_INFO(LABEL, "path %{public}s errno %{public}d.", FIRST_USE_RECORD_JSON.c_str(), errno);
99         return false;
100     }
101     return true;
102 }
103 
IsCfgFileValid(void)104 bool FirstUseDialog::IsCfgFileValid(void)
105 {
106     struct stat fstat = {};
107     if (stat(FIRST_USE_RECORD_JSON.c_str(), &fstat) != 0) {
108         SC_LOG_INFO(LABEL, "path %{public}s errno %{public}d.", FIRST_USE_RECORD_JSON.c_str(), errno);
109         return false;
110     }
111     if (fstat.st_size > MAX_CFG_FILE_SIZE) {
112         SC_LOG_INFO(LABEL, "path %{public}s size too large.", FIRST_USE_RECORD_JSON.c_str());
113         return false;
114     }
115     return true;
116 }
117 
ReadCfgContent(std::string & content)118 bool FirstUseDialog::ReadCfgContent(std::string& content)
119 {
120     std::stringstream buffer;
121     std::ifstream i(FIRST_USE_RECORD_JSON);
122     if (!i.is_open()) {
123         SC_LOG_ERROR(LABEL, "cannot open file %{public}s, errno %{public}d.", FIRST_USE_RECORD_JSON.c_str(), errno);
124         return false;
125     }
126     buffer << i.rdbuf();
127     content = buffer.str();
128     i.close();
129     return true;
130 }
131 
WriteCfgContent(const std::string & content)132 void FirstUseDialog::WriteCfgContent(const std::string& content)
133 {
134     std::ofstream out(FIRST_USE_RECORD_JSON);
135     if (!out.is_open()) {
136         SC_LOG_ERROR(LABEL, "cannot open file %{public}s, errno %{public}d.", FIRST_USE_RECORD_JSON.c_str(), errno);
137         return;
138     }
139     out << content;
140     out.close();
141 }
142 
ParseRecord(nlohmann::json & jsonRes,AccessToken::AccessTokenID & id,uint64_t & type)143 bool FirstUseDialog::ParseRecord(nlohmann::json& jsonRes,
144     AccessToken::AccessTokenID& id, uint64_t& type)
145 {
146     if (jsonRes.find(TOKEN_ID_TAG) == jsonRes.end() ||
147         !jsonRes.at(TOKEN_ID_TAG).is_number()) {
148         SC_LOG_ERROR(LABEL, "parse TokenId failed.");
149         return false;
150     }
151     id = jsonRes.at(TOKEN_ID_TAG).get<uint32_t>();
152     if (id == AccessToken::INVALID_TOKENID) {
153         SC_LOG_ERROR(LABEL, "TokenId is not invalid.");
154         return false;
155     }
156 
157     if (jsonRes.find(COMP_TYPE_TAG) == jsonRes.end() ||
158         !jsonRes.at(COMP_TYPE_TAG).is_number()) {
159         SC_LOG_ERROR(LABEL, "parse CompType failed.");
160         return false;
161     }
162     type = jsonRes.at(COMP_TYPE_TAG).get<uint64_t>();
163     return true;
164 }
165 
ParseRecords(nlohmann::json & jsonRes)166 void FirstUseDialog::ParseRecords(nlohmann::json& jsonRes)
167 {
168     std::unique_lock<std::mutex> lock(useMapMutex_);
169     if (jsonRes.find(FIRST_USE_RECORD_TAG) == jsonRes.end() ||
170         !jsonRes.at(FIRST_USE_RECORD_TAG).is_array()) {
171         SC_LOG_ERROR(LABEL, "parse tag failed.");
172         return;
173     }
174 
175     nlohmann::json recordListJson = jsonRes.at(FIRST_USE_RECORD_TAG);
176     for (auto& recordJson : recordListJson) {
177         AccessToken::AccessTokenID id;
178         uint64_t type;
179         if (!ParseRecord(recordJson, id, type)) {
180             SC_LOG_ERROR(LABEL, "parse record failed.");
181             return;
182         }
183         firstUseMap_[id] = type;
184     }
185 }
186 
LoadFirstUseRecord(void)187 void FirstUseDialog::LoadFirstUseRecord(void)
188 {
189     if (!IsCfgFileValid()) {
190         SC_LOG_INFO(LABEL, "first use record is invalid.");
191         return;
192     }
193 
194     std::string content;
195     if (!ReadCfgContent(content)) {
196         return;
197     }
198 
199     nlohmann::json jsonRes = nlohmann::json::parse(content, nullptr, false);
200     if (jsonRes.is_discarded()) {
201         SC_LOG_ERROR(LABEL, "cfg info format is invalid");
202         return;
203     }
204 
205     ParseRecords(jsonRes);
206 }
207 
SaveFirstUseRecord(void)208 void FirstUseDialog::SaveFirstUseRecord(void)
209 {
210     SC_LOG_INFO(LABEL, "start save first_use_record json");
211     if (!IsCfgDirExist()) {
212         SC_LOG_ERROR(LABEL, "dir %{public}s is not exist, errno %{public}d",
213             SEC_COMP_SRV_CFG_PATH.c_str(), errno);
214         return;
215     }
216 
217     if (!IsCfgFileExist()) {
218         if (creat(FIRST_USE_RECORD_JSON.c_str(), S_IRUSR | S_IWUSR) == -1) {
219             SC_LOG_ERROR(LABEL, "create %{public}s failed, errno %{public}d",
220                 FIRST_USE_RECORD_JSON.c_str(), errno);
221             return;
222         }
223     }
224 
225     nlohmann::json jsonRes;
226     {
227         std::unique_lock<std::mutex> lock(useMapMutex_);
228         nlohmann::json recordsJson;
229         for (auto iter = firstUseMap_.begin(); iter != firstUseMap_.end(); ++iter) {
230             AccessToken::AccessTokenID id = iter->first;
231             AccessToken::HapTokenInfo info;
232             if (AccessToken::AccessTokenKit::GetHapTokenInfo(id, info) != AccessToken::RET_SUCCESS) {
233                 SC_LOG_INFO(LABEL, "token id %{public}d is not exist, do not update it.", id);
234                 continue;
235             }
236             nlohmann::json recordJson;
237             recordJson[TOKEN_ID_TAG] = id;
238             recordJson[COMP_TYPE_TAG] = iter->second;
239             recordsJson.emplace_back(recordJson);
240         }
241 
242         jsonRes[FIRST_USE_RECORD_TAG] = recordsJson;
243     }
244     WriteCfgContent(jsonRes.dump());
245 }
246 
RemoveDialogWaitEntitys(int32_t pid)247 void FirstUseDialog::RemoveDialogWaitEntitys(int32_t pid)
248 {
249     std::unique_lock<std::mutex> lock(useMapMutex_);
250     for (auto iter = dialogWaitMap_.begin(); iter != dialogWaitMap_.end();) {
251         std::shared_ptr<SecCompEntity> entity = iter->second;
252         if ((entity != nullptr) && (entity->pid_ == pid)) {
253             iter = dialogWaitMap_.erase(iter);
254         } else {
255             ++iter;
256         }
257     }
258 }
259 
GrantDialogWaitEntity(int32_t scId)260 int32_t FirstUseDialog::GrantDialogWaitEntity(int32_t scId)
261 {
262     std::unique_lock<std::mutex> lock(useMapMutex_);
263     auto it = dialogWaitMap_.find(scId);
264     if (it == dialogWaitMap_.end()) {
265         SC_LOG_ERROR(LABEL, "Grant dialog wait security component %{public}d is not exist", scId);
266         return SC_SERVICE_ERROR_COMPONENT_NOT_EXIST;
267     }
268 
269     std::shared_ptr<SecCompEntity> sc = it->second;
270     if (sc == nullptr) {
271         SC_LOG_ERROR(LABEL, "Grant dialog wait security component %{public}d is nullptr", scId);
272         return SC_SERVICE_ERROR_COMPONENT_NOT_EXIST;
273     }
274     int32_t res = sc->GrantTempPermission();
275     if (res != SC_OK) {
276         OHOS::AppExecFwk::BundleMgrClient bmsClient;
277         std::string bundleName = "";
278         bmsClient.GetNameForUid(sc->uid_, bundleName);
279         HiSysEventWrite(HiviewDFX::HiSysEvent::Domain::SEC_COMPONENT, "TEMP_GRANT_FAILED",
280             HiviewDFX::HiSysEvent::EventType::FAULT, "CALLER_UID", sc->uid_, "CALLER_BUNDLE_NAME", bundleName,
281             "CALLER_PID", sc->pid_, "SC_ID", scId, "SC_TYPE", sc->GetType());
282     } else {
283         HiSysEventWrite(HiviewDFX::HiSysEvent::Domain::SEC_COMPONENT, "TEMP_GRANT_SUCCESS",
284             HiviewDFX::HiSysEvent::EventType::BEHAVIOR, "CALLER_UID", sc->uid_,
285             "CALLER_PID", sc->pid_, "SC_ID", scId, "SC_TYPE", sc->GetType());
286     }
287     dialogWaitMap_.erase(scId);
288     return res;
289 }
290 
StartDialogAbility(std::shared_ptr<SecCompEntity> entity,sptr<IRemoteObject> callerToken,sptr<IRemoteObject> dialogCallback)291 void FirstUseDialog::StartDialogAbility(std::shared_ptr<SecCompEntity> entity,
292     sptr<IRemoteObject> callerToken, sptr<IRemoteObject> dialogCallback)
293 {
294     int32_t typeNum;
295     SecCompType type = entity->GetType();
296     if (type == LOCATION_COMPONENT) {
297         typeNum = 0;
298     } else if (type == SAVE_COMPONENT) {
299         typeNum = 1;
300     } else {
301         SC_LOG_ERROR(LABEL, "unknown type.");
302         return;
303     }
304     int32_t scId = entity->scId_;
305     SecCompDialogSrvCallback *call = new (std::nothrow)SecCompDialogSrvCallback(scId, entity, dialogCallback);
306     sptr<IRemoteObject> srvCallback = call;
307     if (srvCallback == nullptr) {
308         SC_LOG_ERROR(LABEL, "New SecCompDialogCallback fail");
309         return;
310     }
311     dialogWaitMap_[scId] = entity;
312     AAFwk::Want want;
313     want.SetElementName(GRANT_ABILITY_BUNDLE_NAME, GRANT_ABILITY_ABILITY_NAME);
314     want.SetParam(TYPE_KEY, typeNum);
315     want.SetParam(TOKEN_KEY, callerToken);
316     want.SetParam(CALLBACK_KEY, srvCallback);
317     int32_t uid = IPCSkeleton::GetCallingUid();
318     want.SetParam(CALLER_UID_KEY, uid);
319     int startRes = AAFwk::AbilityManagerClient::GetInstance()->StartExtensionAbility(want, callerToken);
320     SC_LOG_INFO(LABEL, "start ability res %{public}d", startRes);
321     if (startRes != 0) {
322         dialogWaitMap_.erase(scId);
323     }
324 }
325 
SendSaveEventHandler(void)326 void FirstUseDialog::SendSaveEventHandler(void)
327 {
328     std::function<void()> delayed = ([this]() {
329         this->SaveFirstUseRecord();
330     });
331 
332     SC_LOG_INFO(LABEL, "Delay first_use_record json");
333     secHandler_->ProxyPostTask(delayed);
334 }
335 
SetFirstUseMap(std::shared_ptr<SecCompEntity> entity)336 bool FirstUseDialog::SetFirstUseMap(std::shared_ptr<SecCompEntity> entity)
337 {
338     uint64_t typeMask;
339     if (entity == nullptr) {
340         SC_LOG_ERROR(LABEL, "Entity is invalid.");
341         return false;
342     }
343 
344     SecCompType type = entity->GetType();
345     if (type == LOCATION_COMPONENT) {
346         typeMask = LOCATION_BUTTON_FIRST_USE;
347     } else if (type == SAVE_COMPONENT) {
348         typeMask = SAVE_BUTTON_FIRST_USE;
349     } else {
350         SC_LOG_INFO(LABEL, "This type need not notify dialog to user.");
351         return false;
352     }
353 
354     std::unique_lock<std::mutex> lock(useMapMutex_);
355     AccessToken::AccessTokenID tokenId = entity->tokenId_;
356     auto iter = firstUseMap_.find(tokenId);
357     if (iter == firstUseMap_.end()) {
358         firstUseMap_[tokenId] = typeMask;
359         SendSaveEventHandler();
360         return true;
361     }
362 
363     firstUseMap_[tokenId] |= typeMask;
364     SendSaveEventHandler();
365     return true;
366 }
367 
NotifyFirstUseDialog(std::shared_ptr<SecCompEntity> entity,sptr<IRemoteObject> callerToken,sptr<IRemoteObject> dialogCallback)368 int32_t FirstUseDialog::NotifyFirstUseDialog(std::shared_ptr<SecCompEntity> entity,
369     sptr<IRemoteObject> callerToken, sptr<IRemoteObject> dialogCallback)
370 {
371     if (entity == nullptr) {
372         SC_LOG_ERROR(LABEL, "Entity is invalid.");
373         return SC_SERVICE_ERROR_VALUE_INVALID;
374     }
375     if (secHandler_ == nullptr) {
376         SC_LOG_ERROR(LABEL, "event handler invalid.");
377         return SC_SERVICE_ERROR_VALUE_INVALID;
378     }
379     if (callerToken == nullptr) {
380         SC_LOG_INFO(LABEL, "callerToken is null, no need to notify dialog");
381         return SC_SERVICE_ERROR_VALUE_INVALID;
382     }
383 
384     if (dialogCallback == nullptr) {
385         SC_LOG_INFO(LABEL, "DialogCallback is null, no need to notify dialog");
386         return SC_SERVICE_ERROR_VALUE_INVALID;
387     }
388 
389     uint64_t typeMask;
390     SecCompType type = entity->GetType();
391     if (type == LOCATION_COMPONENT) {
392         typeMask = LOCATION_BUTTON_FIRST_USE;
393     } else if (type == SAVE_COMPONENT) {
394         typeMask = SAVE_BUTTON_FIRST_USE;
395     } else {
396         SC_LOG_INFO(LABEL, "this type need not notify dialog to user");
397         return SC_OK;
398     }
399 
400     std::unique_lock<std::mutex> lock(useMapMutex_);
401     AccessToken::AccessTokenID tokenId = entity->tokenId_;
402     auto iter = firstUseMap_.find(tokenId);
403     if (iter == firstUseMap_.end()) {
404         SC_LOG_INFO(LABEL, "has not use record, start dialog");
405         StartDialogAbility(entity, callerToken, dialogCallback);
406         return SC_SERVICE_ERROR_WAIT_FOR_DIALOG_CLOSE;
407     }
408 
409     uint64_t compTypes = firstUseMap_[tokenId];
410     if ((compTypes & typeMask) == typeMask) {
411         SC_LOG_INFO(LABEL, "no need notify again.");
412         return SC_OK;
413     }
414     StartDialogAbility(entity, callerToken, dialogCallback);
415     return SC_SERVICE_ERROR_WAIT_FOR_DIALOG_CLOSE;
416 }
417 
Init(std::shared_ptr<SecEventHandler> secHandler)418 void FirstUseDialog::Init(std::shared_ptr<SecEventHandler> secHandler)
419 {
420     SC_LOG_DEBUG(LABEL, "Init!!");
421     secHandler_ = secHandler;
422     LoadFirstUseRecord();
423 }
424 }  // namespace SecurityComponent
425 }  // namespace Security
426 }  // namespace OHOS
427