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