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 
28 /**
29  * PermissionEnforcer check permissions for AIDL-generated services which use
30  * the @EnforcePermission annotation.
31  *
32  * <p>AIDL services may be annotated with @EnforcePermission which will trigger
33  * the generation of permission check code. This generated code relies on
34  * PermissionEnforcer to validate the permissions. The methods available are
35  * purposely similar to the AIDL annotation syntax.
36  *
37  * @see android.permission.PermissionManager
38  *
39  * @hide
40  */
41 @SystemService(Context.PERMISSION_ENFORCER_SERVICE)
42 public class PermissionEnforcer {
43 
44     private final Context mContext;
45     private static final String ACCESS_DENIED = "Access denied, requires: ";
46 
47     /** Protected constructor. Allows subclasses to instantiate an object
48      *  without using a Context.
49      */
PermissionEnforcer()50     protected PermissionEnforcer() {
51         mContext = null;
52     }
53 
54     /** Constructor, prefer using the fromContext static method when possible */
PermissionEnforcer(@onNull Context context)55     public PermissionEnforcer(@NonNull Context context) {
56         mContext = context;
57     }
58 
59     @PermissionCheckerManager.PermissionResult
checkPermission(@onNull String permission, @NonNull AttributionSource source)60     protected int checkPermission(@NonNull String permission, @NonNull AttributionSource source) {
61         return PermissionChecker.checkPermissionForDataDelivery(
62             mContext, permission, PermissionChecker.PID_UNKNOWN, source, "" /* message */);
63     }
64 
65     @SuppressWarnings("AndroidFrameworkClientSidePermissionCheck")
66     @PermissionCheckerManager.PermissionResult
checkPermission(@onNull String permission, int pid, int uid)67     protected int checkPermission(@NonNull String permission, int pid, int uid) {
68         if (mContext.checkPermission(permission, pid, uid) == PackageManager.PERMISSION_GRANTED) {
69             return PermissionCheckerManager.PERMISSION_GRANTED;
70         }
71         return PermissionCheckerManager.PERMISSION_HARD_DENIED;
72     }
73 
anyAppOps(@onNull String[] permissions)74     private boolean anyAppOps(@NonNull String[] permissions) {
75         for (String permission : permissions) {
76             if (AppOpsManager.permissionToOpCode(permission) != AppOpsManager.OP_NONE) {
77                 return true;
78             }
79         }
80         return false;
81     }
82 
enforcePermission(@onNull String permission, @NonNull AttributionSource source)83     public void enforcePermission(@NonNull String permission, @NonNull
84             AttributionSource source) throws SecurityException {
85         int result = checkPermission(permission, source);
86         if (result != PermissionCheckerManager.PERMISSION_GRANTED) {
87             throw new SecurityException(ACCESS_DENIED + permission);
88         }
89     }
90 
enforcePermission(@onNull String permission, int pid, int uid)91     public void enforcePermission(@NonNull String permission, int pid, int uid)
92             throws SecurityException {
93         if (AppOpsManager.permissionToOpCode(permission) != AppOpsManager.OP_NONE) {
94             AttributionSource source = new AttributionSource(uid, null, null);
95             enforcePermission(permission, source);
96             return;
97         }
98         int result = checkPermission(permission, pid, uid);
99         if (result != PermissionCheckerManager.PERMISSION_GRANTED) {
100             throw new SecurityException(ACCESS_DENIED + permission);
101         }
102     }
103 
enforcePermissionAllOf(@onNull String[] permissions, @NonNull AttributionSource source)104     public void enforcePermissionAllOf(@NonNull String[] permissions,
105             @NonNull AttributionSource source) throws SecurityException {
106         for (String permission : permissions) {
107             int result = checkPermission(permission, source);
108             if (result != PermissionCheckerManager.PERMISSION_GRANTED) {
109                 throw new SecurityException(ACCESS_DENIED + "allOf={"
110                         + String.join(", ", permissions) + "}");
111             }
112         }
113     }
114 
enforcePermissionAllOf(@onNull String[] permissions, int pid, int uid)115     public void enforcePermissionAllOf(@NonNull String[] permissions,
116             int pid, int uid) throws SecurityException {
117         if (anyAppOps(permissions)) {
118             AttributionSource source = new AttributionSource(uid, null, null);
119             enforcePermissionAllOf(permissions, source);
120             return;
121         }
122         for (String permission : permissions) {
123             int result = checkPermission(permission, pid, uid);
124             if (result != PermissionCheckerManager.PERMISSION_GRANTED) {
125                 throw new SecurityException(ACCESS_DENIED + "allOf={"
126                         + String.join(", ", permissions) + "}");
127             }
128         }
129     }
130 
enforcePermissionAnyOf(@onNull String[] permissions, @NonNull AttributionSource source)131     public void enforcePermissionAnyOf(@NonNull String[] permissions,
132             @NonNull AttributionSource source) throws SecurityException {
133         for (String permission : permissions) {
134             int result = checkPermission(permission, source);
135             if (result == PermissionCheckerManager.PERMISSION_GRANTED) {
136                 return;
137             }
138         }
139         throw new SecurityException(ACCESS_DENIED + "anyOf={"
140                 + String.join(", ", permissions) + "}");
141     }
142 
enforcePermissionAnyOf(@onNull String[] permissions, int pid, int uid)143     public void enforcePermissionAnyOf(@NonNull String[] permissions,
144             int pid, int uid) throws SecurityException {
145         if (anyAppOps(permissions)) {
146             AttributionSource source = new AttributionSource(uid, null, null);
147             enforcePermissionAnyOf(permissions, source);
148             return;
149         }
150         for (String permission : permissions) {
151             int result = checkPermission(permission, pid, uid);
152             if (result == PermissionCheckerManager.PERMISSION_GRANTED) {
153                 return;
154             }
155         }
156         throw new SecurityException(ACCESS_DENIED + "anyOf={"
157                 + String.join(", ", permissions) + "}");
158     }
159 
160     /**
161      * Returns a new PermissionEnforcer based on a Context.
162      *
163      * @hide
164      */
fromContext(@onNull Context context)165     public static PermissionEnforcer fromContext(@NonNull Context context) {
166         return context.getSystemService(PermissionEnforcer.class);
167     }
168 }
169