• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2019 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.om;
18 
19 import android.annotation.NonNull;
20 import android.content.om.OverlayInfo;
21 import android.content.om.OverlayableInfo;
22 import android.net.Uri;
23 import android.os.Process;
24 import android.text.TextUtils;
25 import android.util.Pair;
26 import android.util.Slog;
27 
28 import com.android.internal.annotations.VisibleForTesting;
29 import com.android.internal.util.ArrayUtils;
30 import com.android.internal.util.CollectionUtils;
31 import com.android.server.pm.parsing.pkg.AndroidPackage;
32 
33 import java.io.IOException;
34 import java.util.List;
35 import java.util.Map;
36 
37 /**
38  * Performs verification that a calling UID can act on a target package's overlayable.
39  *
40  * Actors requirements are specified in {@link android.content.om.OverlayManager}.
41  *
42  * @hide
43  */
44 public class OverlayActorEnforcer {
45 
46     private final PackageManagerHelper mPackageManager;
47 
48     /**
49      * @return nullable actor result with {@link ActorState} failure status
50      */
getPackageNameForActor(@onNull String actorUriString, @NonNull Map<String, Map<String, String>> namedActors)51     static Pair<String, ActorState> getPackageNameForActor(@NonNull String actorUriString,
52             @NonNull Map<String, Map<String, String>> namedActors) {
53         Uri actorUri = Uri.parse(actorUriString);
54 
55         String actorScheme = actorUri.getScheme();
56         List<String> actorPathSegments = actorUri.getPathSegments();
57         if (!"overlay".equals(actorScheme) || CollectionUtils.size(actorPathSegments) != 1) {
58             return Pair.create(null, ActorState.INVALID_OVERLAYABLE_ACTOR_NAME);
59         }
60 
61         if (namedActors.isEmpty()) {
62             return Pair.create(null, ActorState.NO_NAMED_ACTORS);
63         }
64 
65         String actorNamespace = actorUri.getAuthority();
66         Map<String, String> namespace = namedActors.get(actorNamespace);
67         if (ArrayUtils.isEmpty(namespace)) {
68             return Pair.create(null, ActorState.MISSING_NAMESPACE);
69         }
70 
71         String actorName = actorPathSegments.get(0);
72         String packageName = namespace.get(actorName);
73         if (TextUtils.isEmpty(packageName)) {
74             return Pair.create(null, ActorState.MISSING_ACTOR_NAME);
75         }
76 
77         return Pair.create(packageName, ActorState.ALLOWED);
78     }
79 
OverlayActorEnforcer(@onNull PackageManagerHelper packageManager)80     public OverlayActorEnforcer(@NonNull PackageManagerHelper packageManager) {
81         mPackageManager = packageManager;
82     }
83 
enforceActor(@onNull OverlayInfo overlayInfo, @NonNull String methodName, int callingUid, int userId)84     void enforceActor(@NonNull OverlayInfo overlayInfo, @NonNull String methodName,
85             int callingUid, int userId) throws SecurityException {
86         final ActorState actorState = isAllowedActor(methodName, overlayInfo, callingUid, userId);
87         if (actorState == ActorState.ALLOWED) {
88             return;
89         }
90 
91         final String targetOverlayableName = overlayInfo.targetOverlayableName;
92         final String errorMessage = "UID" + callingUid + " is not allowed to call " + methodName
93                 + " for "
94                 + (TextUtils.isEmpty(targetOverlayableName) ? "" : (targetOverlayableName + " in "))
95                 + overlayInfo.targetPackageName + " for user " + userId;
96         Slog.w(OverlayManagerService.TAG, errorMessage + " because " + actorState);
97         throw new SecurityException(errorMessage);
98     }
99 
100     /**
101      * See {@link OverlayActorEnforcer} class comment for actor requirements.
102      * @return true if the actor is allowed to act on the target overlayInfo
103      */
104     @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
isAllowedActor(String methodName, OverlayInfo overlayInfo, int callingUid, int userId)105     public ActorState isAllowedActor(String methodName, OverlayInfo overlayInfo,
106             int callingUid, int userId) {
107         // Checked first to avoid package not found errors, which are ignored for calls from shell
108         switch (callingUid) {
109             case Process.ROOT_UID:
110             case Process.SYSTEM_UID:
111                 return ActorState.ALLOWED;
112         }
113 
114         final String targetPackageName = overlayInfo.targetPackageName;
115         final AndroidPackage targetPkgInfo = mPackageManager.getPackageForUser(targetPackageName,
116                 userId);
117         if (targetPkgInfo == null) {
118             return ActorState.TARGET_NOT_FOUND;
119         }
120 
121         if (targetPkgInfo.isDebuggable()) {
122             return ActorState.ALLOWED;
123         }
124 
125         String[] callingPackageNames = mPackageManager.getPackagesForUid(callingUid);
126         if (ArrayUtils.isEmpty(callingPackageNames)) {
127             return ActorState.NO_PACKAGES_FOR_UID;
128         }
129 
130         // A target is always an allowed actor for itself
131         if (ArrayUtils.contains(callingPackageNames, targetPackageName)) {
132             return ActorState.ALLOWED;
133         }
134 
135         String targetOverlayableName = overlayInfo.targetOverlayableName;
136 
137         if (TextUtils.isEmpty(targetOverlayableName)) {
138             try {
139                 if (mPackageManager.doesTargetDefineOverlayable(targetPackageName, userId)) {
140                     return ActorState.MISSING_TARGET_OVERLAYABLE_NAME;
141                 } else {
142                     // If there's no overlayable defined, fallback to the legacy permission check
143                     try {
144                         mPackageManager.enforcePermission(
145                                 android.Manifest.permission.CHANGE_OVERLAY_PACKAGES, methodName);
146 
147                         // If the previous method didn't throw, check passed
148                         return ActorState.ALLOWED;
149                     } catch (SecurityException e) {
150                         return ActorState.MISSING_LEGACY_PERMISSION;
151                     }
152                 }
153             } catch (IOException e) {
154                 return ActorState.ERROR_READING_OVERLAYABLE;
155             }
156         }
157 
158         OverlayableInfo targetOverlayable;
159         try {
160             targetOverlayable = mPackageManager.getOverlayableForTarget(targetPackageName,
161                     targetOverlayableName, userId);
162         } catch (IOException e) {
163             return ActorState.UNABLE_TO_GET_TARGET_OVERLAYABLE;
164         }
165 
166         if (targetOverlayable == null) {
167             return ActorState.MISSING_OVERLAYABLE;
168         }
169 
170         String actor = targetOverlayable.actor;
171         if (TextUtils.isEmpty(actor)) {
172             // If there's no actor defined, fallback to the legacy permission check
173             try {
174                 mPackageManager.enforcePermission(
175                         android.Manifest.permission.CHANGE_OVERLAY_PACKAGES, methodName);
176 
177                 // If the previous method didn't throw, check passed
178                 return ActorState.ALLOWED;
179             } catch (SecurityException e) {
180                 return ActorState.MISSING_LEGACY_PERMISSION;
181             }
182         }
183 
184         Map<String, Map<String, String>> namedActors = mPackageManager.getNamedActors();
185         Pair<String, ActorState> actorUriPair = getPackageNameForActor(actor, namedActors);
186         ActorState actorUriState = actorUriPair.second;
187         if (actorUriState != ActorState.ALLOWED) {
188             return actorUriState;
189         }
190 
191         String actorPackageName = actorUriPair.first;
192         AndroidPackage actorPackage = mPackageManager.getPackageForUser(actorPackageName, userId);
193         if (actorPackage == null) {
194             return ActorState.ACTOR_NOT_FOUND;
195         }
196 
197         // Currently only pre-installed apps can be actors
198         if (!actorPackage.isSystem()) {
199             return ActorState.ACTOR_NOT_PREINSTALLED;
200         }
201 
202         if (ArrayUtils.contains(callingPackageNames, actorPackageName)) {
203             return ActorState.ALLOWED;
204         }
205 
206         return ActorState.INVALID_ACTOR;
207     }
208 
209     /**
210      * For easier logging/debugging, a set of all possible failure/success states when running
211      * enforcement.
212      *
213      * The ordering of this enum should be maintained in the order that cases are checked in code,
214      * as this ordering is used inside OverlayActorEnforcerTests.
215      */
216     public enum ActorState {
217         TARGET_NOT_FOUND,
218         NO_PACKAGES_FOR_UID,
219         MISSING_TARGET_OVERLAYABLE_NAME,
220         MISSING_LEGACY_PERMISSION,
221         ERROR_READING_OVERLAYABLE,
222         UNABLE_TO_GET_TARGET_OVERLAYABLE,
223         MISSING_OVERLAYABLE,
224         INVALID_OVERLAYABLE_ACTOR_NAME,
225         NO_NAMED_ACTORS,
226         MISSING_NAMESPACE,
227         MISSING_ACTOR_NAME,
228         ACTOR_NOT_FOUND,
229         ACTOR_NOT_PREINSTALLED,
230         INVALID_ACTOR,
231         ALLOWED
232     }
233 }
234