1 /*
2  * Copyright (C) 2024 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.am;
18 
19 import android.annotation.Nullable;
20 import android.os.Process;
21 import android.os.UserHandle;
22 
23 import com.android.internal.annotations.GuardedBy;
24 import com.android.server.appop.AppOpsService;
25 import com.android.server.pm.permission.AccessCheckDelegate;
26 import com.android.server.pm.permission.PermissionManagerServiceInternal;
27 
28 import java.util.Collections;
29 import java.util.List;
30 
31 class AccessCheckDelegateHelper {
32     private final ActivityManagerGlobalLock mProcLock;
33 
34     @GuardedBy("mProcLock")
35     private final List<ActiveInstrumentation> mActiveInstrumentation;
36 
37     private final AppOpsService mAppOpsService;
38 
39     private final PermissionManagerServiceInternal mPermissionManagerInternal;
40 
41     @GuardedBy("mProcLock")
42     private AccessCheckDelegate mAccessCheckDelegate;
43 
AccessCheckDelegateHelper(ActivityManagerGlobalLock procLock, List<ActiveInstrumentation> activeInstrumentation, AppOpsService appOpsService, PermissionManagerServiceInternal permissionManagerInternal)44     AccessCheckDelegateHelper(ActivityManagerGlobalLock procLock,
45             List<ActiveInstrumentation> activeInstrumentation, AppOpsService appOpsService,
46             PermissionManagerServiceInternal permissionManagerInternal) {
47         mProcLock = procLock;
48         mActiveInstrumentation = activeInstrumentation;
49         mAppOpsService = appOpsService;
50         mPermissionManagerInternal = permissionManagerInternal;
51     }
52 
53     @GuardedBy("mProcLock")
getAccessCheckDelegateLPr(boolean create)54     private AccessCheckDelegate getAccessCheckDelegateLPr(boolean create) {
55         if (create && mAccessCheckDelegate == null) {
56             mAccessCheckDelegate = new AccessCheckDelegate.AccessCheckDelegateImpl();
57             mAppOpsService.setCheckOpsDelegate(mAccessCheckDelegate);
58             mPermissionManagerInternal.setCheckPermissionDelegate(mAccessCheckDelegate);
59         }
60 
61         return mAccessCheckDelegate;
62     }
63 
64     @GuardedBy("mProcLock")
removeAccessCheckDelegateLPr()65     private void removeAccessCheckDelegateLPr() {
66         mAccessCheckDelegate = null;
67         mAppOpsService.setCheckOpsDelegate(null);
68         mPermissionManagerInternal.setCheckPermissionDelegate(null);
69     }
70 
startDelegateShellPermissionIdentity(int delegateUid, @Nullable String[] permissions)71     void startDelegateShellPermissionIdentity(int delegateUid,
72             @Nullable String[] permissions) {
73         if (UserHandle.getCallingAppId() != Process.SHELL_UID
74                 && UserHandle.getCallingAppId() != Process.ROOT_UID) {
75             throw new SecurityException("Only the shell can delegate its permissions");
76         }
77 
78         synchronized (mProcLock) {
79             AccessCheckDelegate delegate = getAccessCheckDelegateLPr(false);
80             if (delegate != null && !delegate.isDelegateAndOwnerUid(delegateUid)) {
81                 throw new SecurityException("Shell can delegate permissions only "
82                         + "to one instrumentation at a time");
83             }
84             final int instrCount = mActiveInstrumentation.size();
85             for (int i = 0; i < instrCount; i++) {
86                 final ActiveInstrumentation instr =
87                         mActiveInstrumentation.get(i);
88                 if (instr.mTargetInfo.uid != delegateUid) {
89                     continue;
90                 }
91 
92                 // If instrumentation started from the shell the connection is not null
93                 if (instr.mUiAutomationConnection == null) {
94                     throw new SecurityException("Shell can delegate its permissions"
95                             + " only to an instrumentation started from the shell");
96                 }
97 
98                 final String packageName = instr.mTargetInfo.packageName;
99                 delegate = getAccessCheckDelegateLPr(true);
100                 delegate.setShellPermissionDelegate(delegateUid, packageName, permissions);
101                 return;
102             }
103         }
104     }
105 
stopDelegateShellPermissionIdentity()106     void stopDelegateShellPermissionIdentity() {
107         if (UserHandle.getCallingAppId() != Process.SHELL_UID
108                 && UserHandle.getCallingAppId() != Process.ROOT_UID) {
109             throw new SecurityException("Only the shell can delegate its permissions");
110         }
111         synchronized (mProcLock) {
112             AccessCheckDelegate delegate = getAccessCheckDelegateLPr(false);
113             if (delegate == null) {
114                 return;
115             }
116 
117             if (!delegate.hasShellPermissionDelegate()) {
118                 return;
119             }
120 
121             delegate.removeShellPermissionDelegate();
122 
123             if (!delegate.hasDelegateOrOverrides()) {
124                 removeAccessCheckDelegateLPr();
125             }
126         }
127     }
128 
getDelegatedShellPermissions()129     List<String> getDelegatedShellPermissions() {
130         if (UserHandle.getCallingAppId() != Process.SHELL_UID
131                 && UserHandle.getCallingAppId() != Process.ROOT_UID) {
132             throw new SecurityException("Only the shell can get delegated permissions");
133         }
134         synchronized (mProcLock) {
135             AccessCheckDelegate delegate = getAccessCheckDelegateLPr(false);
136             if (delegate == null) {
137                 return Collections.EMPTY_LIST;
138             }
139 
140             return delegate.getDelegatedPermissionNames();
141         }
142     }
143 
addOverridePermissionState(int originatingUid, int uid, String permission, int result)144     void addOverridePermissionState(int originatingUid, int uid, String permission, int result) {
145         if (UserHandle.getCallingAppId() != Process.ROOT_UID) {
146             throw new SecurityException("Only root can override permissions");
147         }
148 
149         synchronized (mProcLock) {
150             final int instrCount = mActiveInstrumentation.size();
151             for (int i = 0; i < instrCount; i++) {
152                 final ActiveInstrumentation instr =
153                         mActiveInstrumentation.get(i);
154                 if (instr.mTargetInfo.uid != originatingUid) {
155                     continue;
156                 }
157                 // If instrumentation started from the shell the connection is not null
158                 if (instr.mSourceUid != Process.ROOT_UID || instr.mUiAutomationConnection == null) {
159                     throw new SecurityException("Root can only override permissions only if the "
160                             + "owning app was instrumented from root.");
161                 }
162 
163                 AccessCheckDelegate delegate =
164                         getAccessCheckDelegateLPr(true);
165                 if (delegate.hasOverriddenPermissions()
166                         && !delegate.isDelegateAndOwnerUid(originatingUid)) {
167                     throw new SecurityException("Only one instrumentation to grant"
168                             + " overrides is allowed at a time.");
169                 }
170 
171                 delegate.addOverridePermissionState(originatingUid, uid, permission, result);
172                 return;
173             }
174         }
175     }
176 
removeOverridePermissionState(int originatingUid, int uid, String permission)177     void removeOverridePermissionState(int originatingUid, int uid, String permission) {
178         if (UserHandle.getCallingAppId() != Process.ROOT_UID) {
179             throw new SecurityException("Only root can override permissions.");
180         }
181 
182         synchronized (mProcLock) {
183             AccessCheckDelegate delegate = getAccessCheckDelegateLPr(false);
184             if (delegate == null) {
185                 return;
186             }
187 
188             if (!delegate.isDelegateAndOwnerUid(originatingUid)) {
189                 if (delegate.hasOverriddenPermissions()) {
190                     throw new SecurityException("Only the granter of current overrides can remove "
191                             + "them.");
192                 }
193                 return;
194             }
195 
196             delegate.removeOverridePermissionState(uid, permission);
197 
198             if (!delegate.hasDelegateOrOverrides()) {
199                 removeAccessCheckDelegateLPr();
200             }
201         }
202     }
203 
clearOverridePermissionStates(int originatingUid, int uid)204     void clearOverridePermissionStates(int originatingUid, int uid) {
205         if (UserHandle.getCallingAppId() != Process.ROOT_UID) {
206             throw new SecurityException("Only root can override permissions.");
207         }
208         synchronized (mProcLock) {
209             AccessCheckDelegate delegate = getAccessCheckDelegateLPr(false);
210             if (delegate == null) {
211                 return;
212             }
213 
214             if (!delegate.isDelegateAndOwnerUid(originatingUid)) {
215                 if (delegate.hasOverriddenPermissions()) {
216                     throw new SecurityException(
217                             "Only the granter of current overrides can remove them.");
218                 }
219                 return;
220             }
221 
222             delegate.clearOverridePermissionStates(uid);
223 
224             if (!delegate.hasDelegateOrOverrides()) {
225                 removeAccessCheckDelegateLPr();
226             }
227         }
228     }
229 
clearAllOverridePermissionStates(int originatingUid)230     void clearAllOverridePermissionStates(int originatingUid) {
231         if (UserHandle.getCallingAppId() != Process.ROOT_UID) {
232             throw new SecurityException("Only root can override permissions.");
233         }
234         synchronized (mProcLock) {
235             AccessCheckDelegate delegate = getAccessCheckDelegateLPr(false);
236             if (delegate == null) {
237                 return;
238             }
239 
240             if (!delegate.isDelegateAndOwnerUid(originatingUid)) {
241                 if (delegate.hasOverriddenPermissions()) {
242                     throw new SecurityException(
243                             "Only the granter of current overrides can remove them.");
244                 }
245                 return;
246             }
247 
248             delegate.clearAllOverridePermissionStates();
249 
250             if (!delegate.hasDelegateOrOverrides()) {
251                 removeAccessCheckDelegateLPr();
252             }
253         }
254     }
255 
onInstrumentationFinished(int uid, String packageName)256     void onInstrumentationFinished(int uid, String packageName) {
257         synchronized (mProcLock) {
258             AccessCheckDelegate delegate = getAccessCheckDelegateLPr(false);
259             if (delegate != null) {
260                 if (delegate.isDelegatePackage(uid, packageName)) {
261                     delegate.removeShellPermissionDelegate();
262                 }
263                 if (delegate.isDelegateAndOwnerUid(uid)) {
264                     delegate.clearAllOverridePermissionStates();
265                 }
266                 if (!delegate.hasDelegateOrOverrides()) {
267                     removeAccessCheckDelegateLPr();
268                 }
269             }
270         }
271     }
272 }
273