1 /*
2 * Copyright (c) 2021 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 "distributed_sched_permission.h"
17
18 #include "accesstoken_kit.h"
19 #include "adapter/dnetwork_adapter.h"
20 #include "bundle/bundle_manager_internal.h"
21 #include "caller_info.h"
22 #include "datetime_ex.h"
23 #include "device_auth_defines.h"
24 #include "distributed_sched_adapter.h"
25 #include "dtbschedmgr_log.h"
26 #include "ipc_skeleton.h"
27
28 namespace OHOS {
29 namespace DistributedSchedule {
30 using namespace OHOS::Security;
31 namespace {
32 const std::string TAG = "DistributedSchedPermission";
33 const std::string FOUNDATION_PROCESS_NAME = "foundation";
34 }
35 IMPLEMENT_SINGLE_INSTANCE(DistributedSchedPermission);
from_json(const nlohmann::json & jsonObject,GroupInfo & groupInfo)36 void from_json(const nlohmann::json& jsonObject, GroupInfo& groupInfo)
37 {
38 if (jsonObject.find(FIELD_GROUP_NAME) != jsonObject.end()) {
39 jsonObject.at(FIELD_GROUP_NAME).get_to(groupInfo.groupName);
40 }
41 if (jsonObject.find(FIELD_GROUP_ID) != jsonObject.end()) {
42 jsonObject.at(FIELD_GROUP_ID).get_to(groupInfo.groupId);
43 }
44 if (jsonObject.find(FIELD_GROUP_OWNER) != jsonObject.end()) {
45 jsonObject.at(FIELD_GROUP_OWNER).get_to(groupInfo.groupOwner);
46 }
47 if (jsonObject.find(FIELD_GROUP_TYPE) != jsonObject.end()) {
48 jsonObject.at(FIELD_GROUP_TYPE).get_to(groupInfo.groupType);
49 }
50 if (jsonObject.find(FIELD_GROUP_VISIBILITY) != jsonObject.end()) {
51 jsonObject.at(FIELD_GROUP_VISIBILITY).get_to(groupInfo.groupVisibility);
52 }
53 }
54
CheckDPermission(const AAFwk::Want & want,const CallerInfo & callerInfo,const AccountInfo & accountInfo,const AppExecFwk::AbilityInfo & abilityInfo,const std::string & localDeviceId)55 int32_t DistributedSchedPermission::CheckDPermission(const AAFwk::Want& want, const CallerInfo& callerInfo,
56 const AccountInfo& accountInfo, const AppExecFwk::AbilityInfo& abilityInfo, const std::string& localDeviceId)
57 {
58 if (localDeviceId.empty()) {
59 return INVALID_PARAMETERS_ERR;
60 }
61 AppExecFwk::AbilityInfo targetAbility;
62 bool result = getTargetAbility(want, abilityInfo, localDeviceId, targetAbility, callerInfo);
63 if (!result) {
64 HILOGE("CheckDPermission can not find the target ability");
65 return INVALID_PARAMETERS_ERR;
66 }
67 HILOGD("target ability info bundleName:%{public}s abilityName:%{public}s uri:%{private}s visible:%{public}d",
68 targetAbility.bundleName.c_str(), targetAbility.name.c_str(), targetAbility.uri.c_str(),
69 targetAbility.visible);
70 HILOGD("callerType:%{public}d accountType:%{public}d callerUid:%{public}d AccessTokenID:%{public}u",
71 callerInfo.callerType, accountInfo.accountType, callerInfo.uid, callerInfo.accessToken);
72 // 1.check account access permission in no account networking environment.
73 if (!CheckAccountAccessPermission(callerInfo, accountInfo, targetAbility.bundleName)) {
74 HILOGE("CheckAccountAccessPermission denied or failed!");
75 return DMS_ACCOUNT_ACCESS_PERMISSION_DENIED;
76 }
77 // 2.check component access permission, when the ability is not visible.
78 if (!CheckComponentAccessPermission(targetAbility, callerInfo, accountInfo, want)) {
79 HILOGE("CheckComponentAccessPermission denied or failed! the callee component do not have permission");
80 return DMS_COMPONENT_ACCESS_PERMISSION_DENIED;
81 }
82 // 3.check application custom permissions
83 if (!CheckCustomPermission(targetAbility, callerInfo)) {
84 HILOGE("CheckCustomPermission denied or failed! the caller component do not have permission");
85 return DMS_COMPONENT_ACCESS_PERMISSION_DENIED;
86 }
87 HILOGI("CheckDPermission success!!");
88 return ERR_OK;
89 }
90
GetAccountInfo(const std::string & remoteNetworkId,const CallerInfo & callerInfo,AccountInfo & accountInfo)91 int32_t DistributedSchedPermission::GetAccountInfo(const std::string& remoteNetworkId,
92 const CallerInfo& callerInfo, AccountInfo& accountInfo)
93 {
94 if (remoteNetworkId.empty()) {
95 HILOGE("remoteNetworkId is empty");
96 return ERR_NULL_OBJECT;
97 }
98 std::string udid = DnetworkAdapter::GetInstance()->GetUdidByNetworkId(remoteNetworkId);
99 if (udid.empty()) {
100 HILOGE("udid is empty");
101 return ERR_NULL_OBJECT;
102 }
103 if (!GetRelatedGroups(udid, callerInfo.bundleNames, accountInfo)) {
104 HILOGE("GetRelatedGroups failed");
105 return INVALID_PARAMETERS_ERR;
106 }
107 return ERR_OK;
108 }
109
GetRelatedGroups(const std::string & udid,const std::vector<std::string> & bundleNames,AccountInfo & accountInfo)110 bool DistributedSchedPermission::GetRelatedGroups(const std::string& udid,
111 const std::vector<std::string>& bundleNames, AccountInfo& accountInfo)
112 {
113 for (const auto& bundleName : bundleNames) {
114 std::string returnGroups;
115 if (!DistributedSchedAdapter::GetInstance().GetRelatedGroups(udid, bundleName, returnGroups)) {
116 continue;
117 }
118 std::vector<GroupInfo> groupInfos;
119 if (!ParseGroupInfos(returnGroups, groupInfos)) {
120 continue;
121 }
122 for (const auto& groupInfo : groupInfos) {
123 // check group type is whether (same count or point to point) or not
124 if (groupInfo.groupType != GroupType::IDENTICAL_ACCOUNT_GROUP
125 && groupInfo.groupType != GroupType::PEER_TO_PEER_GROUP) {
126 continue;
127 }
128 accountInfo.groupIdList.push_back(groupInfo.groupId);
129 if (groupInfo.groupType == GroupType::IDENTICAL_ACCOUNT_GROUP
130 && accountInfo.accountType != IDistributedSched::SAME_ACCOUNT_TYPE) {
131 accountInfo.accountType = IDistributedSched::SAME_ACCOUNT_TYPE;
132 }
133 }
134 }
135 if (accountInfo.groupIdList.empty()) {
136 HILOGE("groupIdList is empty");
137 return false;
138 }
139 return true;
140 }
141
ParseGroupInfos(const std::string & returnGroupStr,std::vector<GroupInfo> & groupInfos)142 bool DistributedSchedPermission::ParseGroupInfos(const std::string& returnGroupStr, std::vector<GroupInfo>& groupInfos)
143 {
144 nlohmann::json groupInfoJson = nlohmann::json::parse(returnGroupStr, nullptr, false);
145 if (groupInfoJson.is_discarded()) {
146 HILOGE("returnGroupStr parse failed");
147 return false;
148 }
149 HILOGD("groupInfoJson:%{public}s", groupInfoJson.dump().c_str());
150 groupInfos = groupInfoJson.get<std::vector<GroupInfo>>();
151 if (groupInfos.empty()) {
152 HILOGE("groupInfos is empty");
153 return false;
154 }
155 return true;
156 }
157
getTargetAbility(const AAFwk::Want & want,const AppExecFwk::AbilityInfo & abilityInfo,const std::string & localDeviceId,AppExecFwk::AbilityInfo & targetAbility,const CallerInfo & callerInfo) const158 bool DistributedSchedPermission::getTargetAbility(const AAFwk::Want& want,
159 const AppExecFwk::AbilityInfo& abilityInfo, const std::string& localDeviceId,
160 AppExecFwk::AbilityInfo& targetAbility, const CallerInfo& callerInfo) const
161 {
162 if (!BundleManagerInternal::QueryAbilityInfo(want, targetAbility)) {
163 HILOGE("QueryAbilityInfo failed");
164 return false;
165 }
166 return true;
167 }
168
CheckGetCallerPermission(const AAFwk::Want & want,const CallerInfo & callerInfo,const AccountInfo & accountInfo,const std::string & localDeviceId)169 int32_t DistributedSchedPermission::CheckGetCallerPermission(const AAFwk::Want& want, const CallerInfo& callerInfo,
170 const AccountInfo& accountInfo, const std::string& localDeviceId)
171 {
172 AppExecFwk::AbilityInfo abilityInfo;
173 int32_t result = CheckDPermission(want, callerInfo, accountInfo, abilityInfo, localDeviceId);
174 if (result != ERR_OK) {
175 HILOGE("CheckGetCallerPermission fail, error:%{public}d", result);
176 return result;
177 }
178 std::string appId;
179 if (!BundleManagerInternal::GetCallerAppIdFromBms(want.GetElement().GetBundleName(), appId)) {
180 HILOGE("CheckGetCallerPermission get appId fail");
181 return CALL_PERMISSION_DENIED;
182 }
183 if (appId != callerInfo.callerAppId) {
184 HILOGE("CheckGetCallerPermission appId is different");
185 return CALL_PERMISSION_DENIED;
186 }
187 return ERR_OK;
188 }
189
CheckPermission(uint32_t accessToken,const std::string & permissionName) const190 int32_t DistributedSchedPermission::CheckPermission(uint32_t accessToken, const std::string& permissionName) const
191 {
192 HILOGI("called.");
193 if (!IsFoundationCall(IPCSkeleton::GetCallingTokenID())) {
194 return DMS_PERMISSION_DENIED;
195 }
196 if (IsNativeCall(accessToken)) {
197 return ERR_OK;
198 }
199 if (VerifyPermission(accessToken, permissionName)) {
200 return ERR_OK;
201 }
202 return DMS_PERMISSION_DENIED;
203 }
204
IsFoundationCall(uint32_t accessToken) const205 bool DistributedSchedPermission::IsFoundationCall(uint32_t accessToken) const
206 {
207 if (!IsNativeCall(accessToken)) {
208 return false;
209 }
210 AccessToken::NativeTokenInfo nativeTokenInfo;
211 int32_t result = AccessToken::AccessTokenKit::GetNativeTokenInfo(accessToken, nativeTokenInfo);
212 if (result == ERR_OK && nativeTokenInfo.processName == FOUNDATION_PROCESS_NAME) {
213 return true;
214 }
215 HILOGE("not foundation called, processName:%{private}s", nativeTokenInfo.processName.c_str());
216 return false;
217 }
218
IsNativeCall(uint32_t accessToken) const219 bool DistributedSchedPermission::IsNativeCall(uint32_t accessToken) const
220 {
221 auto tokenType = AccessToken::AccessTokenKit::GetTokenTypeFlag(accessToken);
222 if (tokenType == AccessToken::ATokenTypeEnum::TOKEN_NATIVE) {
223 return true;
224 }
225 HILOGE("not native called.");
226 return false;
227 }
228
VerifyPermission(uint32_t accessToken,const std::string & permissionName) const229 bool DistributedSchedPermission::VerifyPermission(uint32_t accessToken, const std::string& permissionName) const
230 {
231 int32_t result = AccessToken::AccessTokenKit::VerifyAccessToken(accessToken, permissionName);
232 if (result == AccessToken::PermissionState::PERMISSION_DENIED) {
233 HILOGE("permission denied, permissionName:%{public}s", permissionName.c_str());
234 return false;
235 }
236 HILOGD("permission matched.");
237 return true;
238 }
239
CheckAccountAccessPermission(const CallerInfo & callerInfo,const AccountInfo & accountInfo,const std::string & targetBundleName)240 bool DistributedSchedPermission::CheckAccountAccessPermission(const CallerInfo& callerInfo,
241 const AccountInfo& accountInfo, const std::string& targetBundleName)
242 {
243 if (accountInfo.accountType == IDistributedSched::SAME_ACCOUNT_TYPE) {
244 HILOGD("no need to check");
245 return true;
246 }
247 if (targetBundleName.empty() || accountInfo.groupIdList.empty()) {
248 HILOGE("targetBundleName or groupIdList is empty");
249 return false;
250 }
251
252 for (const auto& groupId : accountInfo.groupIdList) {
253 HILOGD("groupId:%{public}s targetBundleName:%{public}s", groupId.c_str(), targetBundleName.c_str());
254 if (DistributedSchedAdapter::GetInstance().CheckAccessToGroup(groupId, targetBundleName)) {
255 return true;
256 }
257 }
258 HILOGE("check account permission failed");
259 return false;
260 }
261
CheckComponentAccessPermission(const AppExecFwk::AbilityInfo & targetAbility,const CallerInfo & callerInfo,const AccountInfo & accountInfo,const AAFwk::Want & want) const262 bool DistributedSchedPermission::CheckComponentAccessPermission(const AppExecFwk::AbilityInfo& targetAbility,
263 const CallerInfo& callerInfo, const AccountInfo& accountInfo, const AAFwk::Want& want) const
264 {
265 // reject directly when in no account networking environment and target ability is not visible,
266 if (!targetAbility.visible) {
267 HILOGE("target ability is not visible, permission denied!");
268 return false;
269 }
270 // when in the migration scenario, make sure the appId is the same.
271 if ((want.GetFlags() & AAFwk::Want::FLAG_ABILITY_CONTINUATION) != 0
272 && !BundleManagerInternal::IsSameAppId(callerInfo.callerAppId, targetAbility.bundleName)) {
273 HILOGE("the appId is different in the migration scenario, permission denied!");
274 return false;
275 }
276 HILOGD("check component permission success");
277 return true;
278 }
279
CheckCustomPermission(const AppExecFwk::AbilityInfo & targetAbility,const CallerInfo & callerInfo) const280 bool DistributedSchedPermission::CheckCustomPermission(const AppExecFwk::AbilityInfo& targetAbility,
281 const CallerInfo& callerInfo) const
282 {
283 const auto& permissions = targetAbility.permissions;
284 if (permissions.empty()) {
285 HILOGD("no need any permission, so granted!");
286 return true;
287 }
288 if (callerInfo.accessToken == 0) {
289 HILOGW("kernel is not support or field is not parsed, so denied!");
290 return false;
291 }
292 int64_t begin = GetTickCount();
293 uint32_t dAccessToken = AccessToken::AccessTokenKit::AllocLocalTokenID(
294 callerInfo.sourceDeviceId, callerInfo.accessToken);
295 HILOGI("[PerformanceTest] AllocLocalTokenID spend %{public}" PRId64 " ms", GetTickCount() - begin);
296 if (dAccessToken == 0) {
297 HILOGE("dAccessTokenID is invalid!");
298 return false;
299 }
300 for (const auto& permission : permissions) {
301 if (permission.empty()) {
302 continue;
303 }
304 int32_t result = AccessToken::AccessTokenKit::VerifyAccessToken(dAccessToken, permission);
305 if (result == AccessToken::PermissionState::PERMISSION_DENIED) {
306 HILOGD("dAccessTokenID:%{public}u, permission:%{public}s denied!", dAccessToken, permission.c_str());
307 return false;
308 }
309 HILOGD("dAccessTokenID:%{public}u, permission:%{public}s matched!", dAccessToken, permission.c_str());
310 }
311 return true;
312 }
313 }
314 }