• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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