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