1 /* 2 * Copyright (C) 2020 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.server.pm.verify.domain; 18 19 import android.Manifest; 20 import android.annotation.NonNull; 21 import android.annotation.Nullable; 22 import android.annotation.UserIdInt; 23 import android.content.Context; 24 import android.content.pm.PackageManager; 25 import android.os.Binder; 26 import android.os.Process; 27 28 import com.android.server.pm.verify.domain.proxy.DomainVerificationProxy; 29 30 public class DomainVerificationEnforcer { 31 32 @NonNull 33 private final Context mContext; 34 35 @NonNull 36 private Callback mCallback; 37 DomainVerificationEnforcer(@onNull Context context)38 public DomainVerificationEnforcer(@NonNull Context context) { 39 mContext = context; 40 } 41 setCallback(@onNull Callback callback)42 public void setCallback(@NonNull Callback callback) { 43 mCallback = callback; 44 } 45 46 /** 47 * Enforced when mutating any state from shell or internally in the system process. 48 */ assertInternal(int callingUid)49 public void assertInternal(int callingUid) { 50 switch (callingUid) { 51 case Process.ROOT_UID: 52 case Process.SHELL_UID: 53 case Process.SYSTEM_UID: 54 break; 55 default: 56 throw new SecurityException( 57 "Caller " + callingUid + " is not allowed to change internal state"); 58 } 59 } 60 61 /** 62 * Enforced when retrieving state for a package. The system, the verifier, and anyone approved 63 * to mutate user selections are allowed through. 64 */ assertApprovedQuerent(int callingUid, @NonNull DomainVerificationProxy proxy)65 public void assertApprovedQuerent(int callingUid, @NonNull DomainVerificationProxy proxy) { 66 switch (callingUid) { 67 case Process.ROOT_UID: 68 case Process.SHELL_UID: 69 case Process.SYSTEM_UID: 70 break; 71 default: 72 if (!proxy.isCallerVerifier(callingUid)) { 73 mContext.enforcePermission(android.Manifest.permission.DUMP, 74 Binder.getCallingPid(), callingUid, 75 "Caller " + callingUid 76 + " is not allowed to query domain verification state"); 77 break; 78 } 79 80 mContext.enforcePermission(android.Manifest.permission.QUERY_ALL_PACKAGES, 81 Binder.getCallingPid(), callingUid, 82 "Caller " + callingUid + " does not hold " 83 + android.Manifest.permission.QUERY_ALL_PACKAGES); 84 break; 85 } 86 } 87 88 /** 89 * Enforced when mutating domain verification state inside an exposed API method. 90 */ assertApprovedVerifier(int callingUid, @NonNull DomainVerificationProxy proxy)91 public void assertApprovedVerifier(int callingUid, @NonNull DomainVerificationProxy proxy) 92 throws SecurityException { 93 boolean isAllowed; 94 switch (callingUid) { 95 case Process.ROOT_UID: 96 case Process.SHELL_UID: 97 case Process.SYSTEM_UID: 98 isAllowed = true; 99 break; 100 default: 101 final int callingPid = Binder.getCallingPid(); 102 boolean isLegacyVerificationAgent = false; 103 if (mContext.checkPermission( 104 android.Manifest.permission.DOMAIN_VERIFICATION_AGENT, callingPid, 105 callingUid) != PackageManager.PERMISSION_GRANTED) { 106 isLegacyVerificationAgent = mContext.checkPermission( 107 android.Manifest.permission.INTENT_FILTER_VERIFICATION_AGENT, 108 callingPid, callingUid) == PackageManager.PERMISSION_GRANTED; 109 if (!isLegacyVerificationAgent) { 110 throw new SecurityException("Caller " + callingUid + " does not hold " 111 + android.Manifest.permission.DOMAIN_VERIFICATION_AGENT); 112 } 113 } 114 115 // If the caller isn't a legacy verifier, it needs the QUERY_ALL permission 116 if (!isLegacyVerificationAgent) { 117 mContext.enforcePermission(android.Manifest.permission.QUERY_ALL_PACKAGES, 118 callingPid, callingUid, "Caller " + callingUid + " does not hold " 119 + android.Manifest.permission.QUERY_ALL_PACKAGES); 120 } 121 122 isAllowed = proxy.isCallerVerifier(callingUid); 123 break; 124 } 125 126 if (!isAllowed) { 127 throw new SecurityException("Caller " + callingUid 128 + " is not the approved domain verification agent"); 129 } 130 } 131 132 /** 133 * Enforced when mutating user selection state inside an exposed API method. 134 */ assertApprovedUserStateQuerent(int callingUid, @UserIdInt int callingUserId, @NonNull String packageName, @UserIdInt int targetUserId)135 public boolean assertApprovedUserStateQuerent(int callingUid, @UserIdInt int callingUserId, 136 @NonNull String packageName, @UserIdInt int targetUserId) throws SecurityException { 137 if (callingUserId != targetUserId) { 138 mContext.enforcePermission( 139 Manifest.permission.INTERACT_ACROSS_USERS, 140 Binder.getCallingPid(), callingUid, 141 "Caller is not allowed to edit other users"); 142 } 143 144 if (!mCallback.doesUserExist(callingUserId)) { 145 throw new SecurityException("User " + callingUserId + " does not exist"); 146 } else if (!mCallback.doesUserExist(targetUserId)) { 147 throw new SecurityException("User " + targetUserId + " does not exist"); 148 } 149 150 return !mCallback.filterAppAccess(packageName, callingUid, targetUserId); 151 } 152 153 /** 154 * Enforced when mutating user selection state inside an exposed API method. 155 */ assertApprovedUserSelector(int callingUid, @UserIdInt int callingUserId, @Nullable String packageName, @UserIdInt int targetUserId)156 public boolean assertApprovedUserSelector(int callingUid, @UserIdInt int callingUserId, 157 @Nullable String packageName, @UserIdInt int targetUserId) throws SecurityException { 158 if (callingUserId != targetUserId) { 159 mContext.enforcePermission( 160 Manifest.permission.INTERACT_ACROSS_USERS, 161 Binder.getCallingPid(), callingUid, 162 "Caller is not allowed to edit other users"); 163 } 164 165 mContext.enforcePermission( 166 android.Manifest.permission.UPDATE_DOMAIN_VERIFICATION_USER_SELECTION, 167 Binder.getCallingPid(), callingUid, 168 "Caller is not allowed to edit user selections"); 169 170 if (!mCallback.doesUserExist(callingUserId)) { 171 throw new SecurityException("User " + callingUserId + " does not exist"); 172 } else if (!mCallback.doesUserExist(targetUserId)) { 173 throw new SecurityException("User " + targetUserId + " does not exist"); 174 } 175 176 if (packageName == null) { 177 return true; 178 } 179 180 return !mCallback.filterAppAccess(packageName, callingUid, targetUserId); 181 } 182 callerIsLegacyUserSelector(int callingUid, @UserIdInt int callingUserId, @NonNull String packageName, @UserIdInt int targetUserId)183 public boolean callerIsLegacyUserSelector(int callingUid, @UserIdInt int callingUserId, 184 @NonNull String packageName, @UserIdInt int targetUserId) { 185 mContext.enforcePermission( 186 android.Manifest.permission.SET_PREFERRED_APPLICATIONS, 187 Binder.getCallingPid(), callingUid, 188 "Caller is not allowed to edit user state"); 189 190 if (callingUserId != targetUserId) { 191 if (mContext.checkPermission( 192 Manifest.permission.INTERACT_ACROSS_USERS, 193 Binder.getCallingPid(), callingUid) != PackageManager.PERMISSION_GRANTED) { 194 // Legacy API did not enforce this, so for backwards compatibility, fail silently 195 return false; 196 } 197 } 198 199 if (!mCallback.doesUserExist(callingUserId)) { 200 throw new SecurityException("User " + callingUserId + " does not exist"); 201 } else if (!mCallback.doesUserExist(targetUserId)) { 202 throw new SecurityException("User " + targetUserId + " does not exist"); 203 } 204 205 return !mCallback.filterAppAccess(packageName, callingUid, targetUserId); 206 } 207 callerIsLegacyUserQuerent(int callingUid, @UserIdInt int callingUserId, @NonNull String packageName, @UserIdInt int targetUserId)208 public boolean callerIsLegacyUserQuerent(int callingUid, @UserIdInt int callingUserId, 209 @NonNull String packageName, @UserIdInt int targetUserId) { 210 if (callingUserId != targetUserId) { 211 // The legacy API enforces the _FULL variant, so maintain that here 212 mContext.enforcePermission( 213 Manifest.permission.INTERACT_ACROSS_USERS_FULL, 214 Binder.getCallingPid(), callingUid, 215 "Caller is not allowed to edit other users"); 216 } 217 218 if (!mCallback.doesUserExist(callingUserId)) { 219 throw new SecurityException("User " + callingUserId + " does not exist"); 220 } else if (!mCallback.doesUserExist(targetUserId)) { 221 throw new SecurityException("User " + targetUserId + " does not exist"); 222 } 223 224 return !mCallback.filterAppAccess(packageName, callingUid, targetUserId); 225 } 226 227 /** 228 * Querying for the owners of a domain. Because this API cannot filter the returned list of 229 * packages, enforces {@link android.Manifest.permission.QUERY_ALL_PACKAGES}, but also enforces 230 * {@link android.Manifest.permission.INTERACT_ACROSS_USERS} because each user has a different 231 * state. 232 */ assertOwnerQuerent(int callingUid, @UserIdInt int callingUserId, @UserIdInt int targetUserId)233 public void assertOwnerQuerent(int callingUid, @UserIdInt int callingUserId, 234 @UserIdInt int targetUserId) { 235 final int callingPid = Binder.getCallingPid(); 236 if (callingUserId != targetUserId) { 237 mContext.enforcePermission(android.Manifest.permission.INTERACT_ACROSS_USERS, 238 callingPid, callingUid, "Caller is not allowed to query other users"); 239 } 240 241 mContext.enforcePermission(android.Manifest.permission.QUERY_ALL_PACKAGES, 242 callingPid, callingUid, "Caller " + callingUid + " does not hold " 243 + android.Manifest.permission.QUERY_ALL_PACKAGES); 244 245 mContext.enforcePermission( 246 android.Manifest.permission.UPDATE_DOMAIN_VERIFICATION_USER_SELECTION, 247 callingPid, callingUid, "Caller is not allowed to query user selections"); 248 249 if (!mCallback.doesUserExist(callingUserId)) { 250 throw new SecurityException("User " + callingUserId + " does not exist"); 251 } else if (!mCallback.doesUserExist(targetUserId)) { 252 throw new SecurityException("User " + targetUserId + " does not exist"); 253 } 254 } 255 256 public interface Callback { 257 /** 258 * @return true if access to the given package should be filtered and the method failed as 259 * if the package was not installed 260 */ filterAppAccess(@onNull String packageName, int callingUid, @UserIdInt int userId)261 boolean filterAppAccess(@NonNull String packageName, int callingUid, @UserIdInt int userId); 262 doesUserExist(@serIdInt int userId)263 boolean doesUserExist(@UserIdInt int userId); 264 } 265 } 266