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