1 /* 2 * Copyright (C) 2022 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 android.os; 18 19 import android.annotation.NonNull; 20 import android.annotation.SystemService; 21 import android.app.AppOpsManager; 22 import android.content.AttributionSource; 23 import android.content.Context; 24 import android.content.PermissionChecker; 25 import android.content.pm.PackageManager; 26 import android.permission.PermissionCheckerManager; 27 import android.permission.PermissionManager; 28 29 /** 30 * PermissionEnforcer check permissions for AIDL-generated services which use 31 * the @EnforcePermission annotation. 32 * 33 * <p>AIDL services may be annotated with @EnforcePermission which will trigger 34 * the generation of permission check code. This generated code relies on 35 * PermissionEnforcer to validate the permissions. The methods available are 36 * purposely similar to the AIDL annotation syntax. 37 * 38 * <p>The constructor of the Stub generated by AIDL expects a 39 * PermissionEnforcer. It can be based on the current Context. For example: 40 * 41 * <pre>{@code 42 * class MyFoo extends Foo.Stub { 43 * MyFoo(Context context) { 44 * super(PermissionEnforcer.fromContext(context)); 45 * } 46 * 47 * @Override 48 * @EnforcePermission(android.Manifest.permission.INTERNET) 49 * public MyMethod() { 50 * MyMethod_enforcePermission(); 51 * } 52 * } 53 * }</pre> 54 * 55 * <p>A {@link android.os.test.FakePermissionEnforcer} is available for unit 56 * testing. It can be attached to a mocked Context using: 57 * <pre>{@code 58 * @Mock private Context mContext; 59 * 60 * @Before 61 * public setUp() { 62 * fakeEnforcer = new FakePermissionEnforcer(); 63 * fakeEnforcer.grant(android.Manifest.permission.INTERNET); 64 * 65 * doReturn(fakeEnforcer).when(mContext).getSystemService( 66 eq(Context.PERMISSION_ENFORCER_SERVICE)); 67 * } 68 * }</pre> 69 * 70 * @see android.permission.PermissionManager 71 * 72 * @hide 73 */ 74 @SystemService(Context.PERMISSION_ENFORCER_SERVICE) 75 @android.ravenwood.annotation.RavenwoodKeepWholeClass 76 public class PermissionEnforcer { 77 78 private final Context mContext; 79 private static final String ACCESS_DENIED = "Access denied, requires: "; 80 81 /** Protected constructor. Allows subclasses to instantiate an object 82 * without using a Context. 83 */ PermissionEnforcer()84 protected PermissionEnforcer() { 85 mContext = null; 86 } 87 88 /** Constructor, prefer using the fromContext static method when possible */ 89 @android.ravenwood.annotation.RavenwoodThrow(blockedBy = PermissionManager.class, 90 reason = "Use subclass for unit tests, such as FakePermissionEnforcer") PermissionEnforcer(@onNull Context context)91 public PermissionEnforcer(@NonNull Context context) { 92 mContext = context; 93 } 94 95 @PermissionCheckerManager.PermissionResult checkPermission(@onNull String permission, @NonNull AttributionSource source)96 protected int checkPermission(@NonNull String permission, @NonNull AttributionSource source) { 97 return PermissionChecker.checkPermissionForDataDelivery( 98 mContext, permission, PermissionChecker.PID_UNKNOWN, source, "" /* message */); 99 } 100 101 @SuppressWarnings("AndroidFrameworkClientSidePermissionCheck") 102 @PermissionCheckerManager.PermissionResult checkPermission(@onNull String permission, int pid, int uid)103 protected int checkPermission(@NonNull String permission, int pid, int uid) { 104 if (mContext.checkPermission(permission, pid, uid) == PackageManager.PERMISSION_GRANTED) { 105 return PermissionCheckerManager.PERMISSION_GRANTED; 106 } 107 return PermissionCheckerManager.PERMISSION_HARD_DENIED; 108 } 109 110 @android.ravenwood.annotation.RavenwoodReplace(blockedBy = AppOpsManager.class, 111 reason = "Blocked on Mainline dependencies") permissionToOpCode(String permission)112 private static int permissionToOpCode(String permission) { 113 return AppOpsManager.permissionToOpCode(permission); 114 } 115 permissionToOpCode$ravenwood(String permission)116 private static int permissionToOpCode$ravenwood(String permission) { 117 return AppOpsManager.OP_NONE; 118 } 119 anyAppOps(@onNull String[] permissions)120 private boolean anyAppOps(@NonNull String[] permissions) { 121 for (String permission : permissions) { 122 if (permissionToOpCode(permission) != AppOpsManager.OP_NONE) { 123 return true; 124 } 125 } 126 return false; 127 } 128 enforcePermission(@onNull String permission, @NonNull AttributionSource source)129 public void enforcePermission(@NonNull String permission, @NonNull 130 AttributionSource source) throws SecurityException { 131 int result = checkPermission(permission, source); 132 if (result != PermissionCheckerManager.PERMISSION_GRANTED) { 133 throw new SecurityException(ACCESS_DENIED + permission); 134 } 135 } 136 enforcePermission(@onNull String permission, int pid, int uid)137 public void enforcePermission(@NonNull String permission, int pid, int uid) 138 throws SecurityException { 139 if (permissionToOpCode(permission) != AppOpsManager.OP_NONE) { 140 AttributionSource source = new AttributionSource(uid, null, null); 141 enforcePermission(permission, source); 142 return; 143 } 144 int result = checkPermission(permission, pid, uid); 145 if (result != PermissionCheckerManager.PERMISSION_GRANTED) { 146 throw new SecurityException(ACCESS_DENIED + permission); 147 } 148 } 149 enforcePermissionAllOf(@onNull String[] permissions, @NonNull AttributionSource source)150 public void enforcePermissionAllOf(@NonNull String[] permissions, 151 @NonNull AttributionSource source) throws SecurityException { 152 for (String permission : permissions) { 153 int result = checkPermission(permission, source); 154 if (result != PermissionCheckerManager.PERMISSION_GRANTED) { 155 throw new SecurityException(ACCESS_DENIED + "allOf={" 156 + String.join(", ", permissions) + "}"); 157 } 158 } 159 } 160 enforcePermissionAllOf(@onNull String[] permissions, int pid, int uid)161 public void enforcePermissionAllOf(@NonNull String[] permissions, 162 int pid, int uid) throws SecurityException { 163 if (anyAppOps(permissions)) { 164 AttributionSource source = new AttributionSource(uid, null, null); 165 enforcePermissionAllOf(permissions, source); 166 return; 167 } 168 for (String permission : permissions) { 169 int result = checkPermission(permission, pid, uid); 170 if (result != PermissionCheckerManager.PERMISSION_GRANTED) { 171 throw new SecurityException(ACCESS_DENIED + "allOf={" 172 + String.join(", ", permissions) + "}"); 173 } 174 } 175 } 176 enforcePermissionAnyOf(@onNull String[] permissions, @NonNull AttributionSource source)177 public void enforcePermissionAnyOf(@NonNull String[] permissions, 178 @NonNull AttributionSource source) throws SecurityException { 179 for (String permission : permissions) { 180 int result = checkPermission(permission, source); 181 if (result == PermissionCheckerManager.PERMISSION_GRANTED) { 182 return; 183 } 184 } 185 throw new SecurityException(ACCESS_DENIED + "anyOf={" 186 + String.join(", ", permissions) + "}"); 187 } 188 enforcePermissionAnyOf(@onNull String[] permissions, int pid, int uid)189 public void enforcePermissionAnyOf(@NonNull String[] permissions, 190 int pid, int uid) throws SecurityException { 191 if (anyAppOps(permissions)) { 192 AttributionSource source = new AttributionSource(uid, null, null); 193 enforcePermissionAnyOf(permissions, source); 194 return; 195 } 196 for (String permission : permissions) { 197 int result = checkPermission(permission, pid, uid); 198 if (result == PermissionCheckerManager.PERMISSION_GRANTED) { 199 return; 200 } 201 } 202 throw new SecurityException(ACCESS_DENIED + "anyOf={" 203 + String.join(", ", permissions) + "}"); 204 } 205 206 /** 207 * Returns a new PermissionEnforcer based on a Context. 208 * 209 * @hide 210 */ fromContext(@onNull Context context)211 public static PermissionEnforcer fromContext(@NonNull Context context) { 212 return (PermissionEnforcer) context.getSystemService(Context.PERMISSION_ENFORCER_SERVICE); 213 } 214 } 215