blob: 40efb7cd96d70e153242bd79b8e4dddab207de07 [file] [log] [blame]
Winsond9d17362019-10-02 12:41:29 -07001/*
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
17package com.android.server.om;
18
19import android.annotation.NonNull;
Winsond9d17362019-10-02 12:41:29 -070020import android.content.om.OverlayInfo;
21import android.content.om.OverlayableInfo;
22import android.content.pm.ApplicationInfo;
23import android.content.pm.PackageInfo;
24import android.net.Uri;
25import android.os.Process;
26import android.os.RemoteException;
27import android.text.TextUtils;
Winson Chiu2fdaf812019-12-13 20:01:15 +000028import android.util.Pair;
Winsond9d17362019-10-02 12:41:29 -070029
30import com.android.internal.util.ArrayUtils;
31import com.android.internal.util.CollectionUtils;
32import com.android.server.SystemConfig;
33
34import java.io.IOException;
35import java.util.List;
36import java.util.Map;
37
38/**
39 * Performs verification that a calling UID can act on a target package's overlayable.
40 *
41 * @hide
42 */
43public class OverlayActorEnforcer {
44
Winson792a0aa2020-01-15 17:16:14 -080045 // By default, the reason is not logged to prevent leaks of why it failed
46 private static final boolean DEBUG_REASON = false;
47
Winsonf56ade32019-12-04 11:32:41 -080048 private final OverlayableInfoCallback mOverlayableCallback;
Winsond9d17362019-10-02 12:41:29 -070049
Winson Chiu2fdaf812019-12-13 20:01:15 +000050 /**
51 * @return nullable actor result with {@link ActorState} failure status
52 */
53 static Pair<String, ActorState> getPackageNameForActor(String actorUriString,
54 Map<String, Map<String, String>> namedActors) {
Winson Chiu2fdaf812019-12-13 20:01:15 +000055 Uri actorUri = Uri.parse(actorUriString);
56
57 String actorScheme = actorUri.getScheme();
58 List<String> actorPathSegments = actorUri.getPathSegments();
59 if (!"overlay".equals(actorScheme) || CollectionUtils.size(actorPathSegments) != 1) {
60 return Pair.create(null, ActorState.INVALID_OVERLAYABLE_ACTOR_NAME);
61 }
62
Winsonf1fb9e72020-01-27 17:01:11 -080063 if (namedActors.isEmpty()) {
64 return Pair.create(null, ActorState.NO_NAMED_ACTORS);
65 }
66
Winson Chiu2fdaf812019-12-13 20:01:15 +000067 String actorNamespace = actorUri.getAuthority();
68 Map<String, String> namespace = namedActors.get(actorNamespace);
69 if (namespace == null) {
70 return Pair.create(null, ActorState.MISSING_NAMESPACE);
71 }
72
73 String actorName = actorPathSegments.get(0);
74 String packageName = namespace.get(actorName);
75 if (TextUtils.isEmpty(packageName)) {
76 return Pair.create(null, ActorState.MISSING_ACTOR_NAME);
77 }
78
79 return Pair.create(packageName, ActorState.ALLOWED);
80 }
81
Winsonf56ade32019-12-04 11:32:41 -080082 public OverlayActorEnforcer(@NonNull OverlayableInfoCallback overlayableCallback) {
83 mOverlayableCallback = overlayableCallback;
Winsond9d17362019-10-02 12:41:29 -070084 }
85
86 void enforceActor(@NonNull OverlayInfo overlayInfo, @NonNull String methodName,
87 int callingUid, int userId) throws SecurityException {
88 ActorState actorState = isAllowedActor(methodName, overlayInfo, callingUid, userId);
89 if (actorState == ActorState.ALLOWED) {
90 return;
91 }
92
93 String targetOverlayableName = overlayInfo.targetOverlayableName;
94 throw new SecurityException("UID" + callingUid + " is not allowed to call "
95 + methodName + " for "
96 + (TextUtils.isEmpty(targetOverlayableName) ? "" : (targetOverlayableName + " in "))
Winson792a0aa2020-01-15 17:16:14 -080097 + overlayInfo.targetPackageName + (DEBUG_REASON ? (" because " + actorState) : "")
Winsond9d17362019-10-02 12:41:29 -070098 );
99 }
100
101 /**
102 * An actor is valid if any of the following is true:
103 * - is {@link Process#ROOT_UID}, {@link Process#SYSTEM_UID}
104 * - is the target overlay package
105 * - has the CHANGE_OVERLAY_PACKAGES permission and an actor is not defined
106 * - is the same the as the package defined in {@link SystemConfig#getNamedActors()} for a given
107 * namespace and actor name
108 *
109 * @return true if the actor is allowed to act on the target overlayInfo
110 */
111 private ActorState isAllowedActor(String methodName, OverlayInfo overlayInfo,
112 int callingUid, int userId) {
113 switch (callingUid) {
114 case Process.ROOT_UID:
115 case Process.SYSTEM_UID:
116 return ActorState.ALLOWED;
117 }
118
Winsonf56ade32019-12-04 11:32:41 -0800119 String[] callingPackageNames = mOverlayableCallback.getPackagesForUid(callingUid);
Winsond9d17362019-10-02 12:41:29 -0700120 if (ArrayUtils.isEmpty(callingPackageNames)) {
121 return ActorState.NO_PACKAGES_FOR_UID;
122 }
123
124 // A target is always an allowed actor for itself
125 String targetPackageName = overlayInfo.targetPackageName;
126 if (ArrayUtils.contains(callingPackageNames, targetPackageName)) {
127 return ActorState.ALLOWED;
128 }
129
130 String targetOverlayableName = overlayInfo.targetOverlayableName;
131
132 if (TextUtils.isEmpty(targetOverlayableName)) {
133 try {
Winsonf56ade32019-12-04 11:32:41 -0800134 if (mOverlayableCallback.doesTargetDefineOverlayable(targetPackageName, userId)) {
Winsond9d17362019-10-02 12:41:29 -0700135 return ActorState.MISSING_TARGET_OVERLAYABLE_NAME;
136 } else {
137 // If there's no overlayable defined, fallback to the legacy permission check
138 try {
Winsonf56ade32019-12-04 11:32:41 -0800139 mOverlayableCallback.enforcePermission(
Winsond9d17362019-10-02 12:41:29 -0700140 android.Manifest.permission.CHANGE_OVERLAY_PACKAGES, methodName);
141
142 // If the previous method didn't throw, check passed
143 return ActorState.ALLOWED;
144 } catch (SecurityException e) {
145 return ActorState.MISSING_LEGACY_PERMISSION;
146 }
147 }
148 } catch (RemoteException | IOException e) {
149 return ActorState.ERROR_READING_OVERLAYABLE;
150 }
151 }
152
153 OverlayableInfo targetOverlayable;
154 try {
Winsonf56ade32019-12-04 11:32:41 -0800155 targetOverlayable = mOverlayableCallback.getOverlayableForTarget(targetPackageName,
Winsond9d17362019-10-02 12:41:29 -0700156 targetOverlayableName, userId);
157 } catch (IOException e) {
158 return ActorState.UNABLE_TO_GET_TARGET;
159 }
160
161 if (targetOverlayable == null) {
162 return ActorState.MISSING_OVERLAYABLE;
163 }
164
165 String actor = targetOverlayable.actor;
166 if (TextUtils.isEmpty(actor)) {
167 // If there's no actor defined, fallback to the legacy permission check
168 try {
Winsonf56ade32019-12-04 11:32:41 -0800169 mOverlayableCallback.enforcePermission(
Winsond9d17362019-10-02 12:41:29 -0700170 android.Manifest.permission.CHANGE_OVERLAY_PACKAGES, methodName);
171
172 // If the previous method didn't throw, check passed
173 return ActorState.ALLOWED;
174 } catch (SecurityException e) {
175 return ActorState.MISSING_LEGACY_PERMISSION;
176 }
177 }
178
Winsonf56ade32019-12-04 11:32:41 -0800179 Map<String, Map<String, String>> namedActors = mOverlayableCallback.getNamedActors();
Winson Chiu2fdaf812019-12-13 20:01:15 +0000180 Pair<String, ActorState> actorUriPair = getPackageNameForActor(actor, namedActors);
181 ActorState actorUriState = actorUriPair.second;
182 if (actorUriState != ActorState.ALLOWED) {
183 return actorUriState;
Winsond9d17362019-10-02 12:41:29 -0700184 }
185
Winson Chiu2fdaf812019-12-13 20:01:15 +0000186 String packageName = actorUriPair.first;
Winsonf56ade32019-12-04 11:32:41 -0800187 PackageInfo packageInfo = mOverlayableCallback.getPackageInfo(packageName, userId);
Winsond9d17362019-10-02 12:41:29 -0700188 if (packageInfo == null) {
189 return ActorState.MISSING_APP_INFO;
190 }
191
192 ApplicationInfo appInfo = packageInfo.applicationInfo;
193 if (appInfo == null) {
194 return ActorState.MISSING_APP_INFO;
195 }
196
197 // Currently only pre-installed apps can be actors
198 if (!appInfo.isSystemApp() && !appInfo.isUpdatedSystemApp()) {
199 return ActorState.ACTOR_NOT_PREINSTALLED;
200 }
201
202 if (ArrayUtils.contains(callingPackageNames, packageName)) {
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 */
Winsonf56ade32019-12-04 11:32:41 -0800213 public enum ActorState {
Winsond9d17362019-10-02 12:41:29 -0700214 ALLOWED,
215 INVALID_ACTOR,
216 MISSING_NAMESPACE,
217 MISSING_PACKAGE,
218 MISSING_APP_INFO,
219 ACTOR_NOT_PREINSTALLED,
220 NO_PACKAGES_FOR_UID,
221 MISSING_ACTOR_NAME,
222 ERROR_READING_OVERLAYABLE,
223 MISSING_TARGET_OVERLAYABLE_NAME,
224 MISSING_OVERLAYABLE,
225 INVALID_OVERLAYABLE_ACTOR_NAME,
226 NO_NAMED_ACTORS,
227 UNABLE_TO_GET_TARGET,
228 MISSING_LEGACY_PERMISSION
229 }
Winsond9d17362019-10-02 12:41:29 -0700230}