1 /*
2  * Copyright (C) 2016 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 static com.android.server.om.OverlayManagerService.DEBUG;
20 import static com.android.server.om.OverlayManagerService.TAG;
21 
22 import android.annotation.IntDef;
23 import android.annotation.NonNull;
24 import android.annotation.UserIdInt;
25 import android.content.om.OverlayInfo;
26 import android.content.om.OverlayableInfo;
27 import android.os.Build.VERSION_CODES;
28 import android.os.FabricatedOverlayInfo;
29 import android.os.FabricatedOverlayInternal;
30 import android.os.OverlayablePolicy;
31 import android.os.SystemProperties;
32 import android.text.TextUtils;
33 import android.util.Slog;
34 
35 import com.android.server.pm.pkg.AndroidPackage;
36 import com.android.server.pm.pkg.PackageState;
37 
38 import java.io.IOException;
39 import java.lang.annotation.Retention;
40 import java.lang.annotation.RetentionPolicy;
41 import java.util.List;
42 
43 /**
44  * Handle the creation and deletion of idmap files.
45  *
46  * The actual work is performed by idmap2d.
47  * @see IdmapDaemon
48  */
49 final class IdmapManager {
50     private static final boolean VENDOR_IS_Q_OR_LATER;
51     static {
52         final String value = SystemProperties.get("ro.vndk.version", "29");
53         boolean isQOrLater;
54         try {
55             isQOrLater = Integer.parseInt(value) >= 29;
56         } catch (NumberFormatException e) {
57             // The version is not a number, therefore it is a development codename.
58             isQOrLater = true;
59         }
60 
61         VENDOR_IS_Q_OR_LATER = isQOrLater;
62     }
63 
64     static final int IDMAP_NOT_EXIST = 0;
65     static final int IDMAP_IS_VERIFIED = 1;
66     static final int IDMAP_IS_MODIFIED = 1 << 1;
67 
68     @IntDef(flag = true, prefix = { "IDMAP_" }, value = {
69             IDMAP_NOT_EXIST,
70             IDMAP_IS_VERIFIED,
71             IDMAP_IS_MODIFIED,
72     })
73     @Retention(RetentionPolicy.SOURCE)
74     public @interface IdmapStatus {}
75 
76     private final IdmapDaemon mIdmapDaemon;
77     private final PackageManagerHelper mPackageManager;
78 
79     /**
80      * Package name of the reference package defined in 'overlay-config-signature' tag of
81      * SystemConfig or empty String if tag not defined. This package is vetted on scan by
82      * PackageManagerService that it's a system package and is used to check if overlay matches
83      * its signature in order to fulfill the config_signature policy.
84      */
85     private final String mConfigSignaturePackage;
86 
IdmapManager(final IdmapDaemon idmapDaemon, final PackageManagerHelper packageManager)87     IdmapManager(final IdmapDaemon idmapDaemon, final PackageManagerHelper packageManager) {
88         mPackageManager = packageManager;
89         mIdmapDaemon = idmapDaemon;
90         mConfigSignaturePackage = packageManager.getConfigSignaturePackage();
91     }
92 
93     /**
94      * Creates the idmap for the target/overlay combination and returns whether the idmap file was
95      * modified.
96      * @return the status of the specific idmap file. It's one of the following.<ul>
97      *     <li>{@link #IDMAP_NOT_EXIST} means the idmap file is not existed.</li>
98      *     <li>{@link #IDMAP_IS_VERIFIED} means the idmap file is verified by Idmap2d.</li>
99      *     <li>{@link #IDMAP_IS_MODIFIED | IDMAP_IS_VERIFIED } means the idmap file is modified and
100      *     verified by Idmap2d.</li>
101      * </ul>.
102      */
createIdmap(@onNull final AndroidPackage targetPackage, @NonNull PackageState overlayPackageState, @NonNull final AndroidPackage overlayPackage, String overlayBasePath, String overlayName, @UserIdInt int userId)103     @IdmapStatus int createIdmap(@NonNull final AndroidPackage targetPackage,
104             @NonNull PackageState overlayPackageState, @NonNull final AndroidPackage overlayPackage,
105             String overlayBasePath, String overlayName, @UserIdInt int userId) {
106         if (DEBUG) {
107             Slog.d(TAG, "create idmap for " + targetPackage.getPackageName() + " and "
108                     + overlayPackage.getPackageName());
109         }
110         final String targetPath = targetPackage.getSplits().get(0).getPath();
111         try {
112             int policies = calculateFulfilledPolicies(targetPackage, overlayPackageState,
113                     overlayPackage, userId);
114             boolean enforce = enforceOverlayable(overlayPackageState, overlayPackage);
115             if (mIdmapDaemon.verifyIdmap(targetPath, overlayBasePath, overlayName, policies,
116                     enforce, userId)) {
117                 return IDMAP_IS_VERIFIED;
118             }
119             final boolean idmapCreated = mIdmapDaemon.createIdmap(targetPath, overlayBasePath,
120                     overlayName, policies, enforce, userId) != null;
121             return (idmapCreated) ? IDMAP_IS_MODIFIED | IDMAP_IS_VERIFIED : IDMAP_NOT_EXIST;
122         } catch (Exception e) {
123             Slog.w(TAG, "failed to generate idmap for " + targetPath + " and "
124                     + overlayBasePath, e);
125             return IDMAP_NOT_EXIST;
126         }
127     }
128 
removeIdmap(@onNull final OverlayInfo oi, final int userId)129     boolean removeIdmap(@NonNull final OverlayInfo oi, final int userId) {
130         if (DEBUG) {
131             Slog.d(TAG, "remove idmap for " + oi.baseCodePath);
132         }
133         try {
134             return mIdmapDaemon.removeIdmap(oi.baseCodePath, userId);
135         } catch (Exception e) {
136             Slog.w(TAG, "failed to remove idmap for " + oi.baseCodePath, e);
137             return false;
138         }
139     }
140 
idmapExists(@onNull final OverlayInfo oi)141     boolean idmapExists(@NonNull final OverlayInfo oi) {
142         return mIdmapDaemon.idmapExists(oi.baseCodePath, oi.userId);
143     }
144 
145     /**
146      * @return the list of all fabricated overlays
147      */
getFabricatedOverlayInfos()148     List<FabricatedOverlayInfo> getFabricatedOverlayInfos() {
149         return mIdmapDaemon.getFabricatedOverlayInfos();
150     }
151 
152     /**
153      * Creates a fabricated overlay and persists it to disk.
154      * @return the path to the fabricated overlay
155      */
createFabricatedOverlay(@onNull FabricatedOverlayInternal overlay)156     FabricatedOverlayInfo createFabricatedOverlay(@NonNull FabricatedOverlayInternal overlay) {
157         return mIdmapDaemon.createFabricatedOverlay(overlay);
158     }
159 
160     /**
161      * Deletes the fabricated overlay file on disk.
162      * @return whether the path was deleted
163      */
deleteFabricatedOverlay(@onNull String path)164     boolean deleteFabricatedOverlay(@NonNull String path) {
165         return mIdmapDaemon.deleteFabricatedOverlay(path);
166     }
167 
168     /**
169      * Gets the idmap data associated with an overlay, in dump format.
170      * Only indented for debugging.
171      */
dumpIdmap(@onNull String overlayPath)172     String dumpIdmap(@NonNull String overlayPath) {
173         return mIdmapDaemon.dumpIdmap(overlayPath);
174     }
175 
176     /**
177      * Checks if overlayable and policies should be enforced on the specified overlay for backwards
178      * compatibility with pre-Q overlays.
179      */
enforceOverlayable(@onNull PackageState overlayPackageState, @NonNull final AndroidPackage overlayPackage)180     private boolean enforceOverlayable(@NonNull PackageState overlayPackageState,
181             @NonNull final AndroidPackage overlayPackage) {
182         if (overlayPackage.getTargetSdkVersion() >= VERSION_CODES.Q) {
183             // Always enforce policies for overlays targeting Q+.
184             return true;
185         }
186 
187         if (overlayPackageState.isVendor()) {
188             // If the overlay is on a pre-Q vendor partition, do not enforce overlayable
189             // restrictions on this overlay because the pre-Q platform has no understanding of
190             // overlayable.
191             return VENDOR_IS_Q_OR_LATER;
192         }
193 
194         // Do not enforce overlayable restrictions on pre-Q overlays that are signed with the
195         // platform signature or that are preinstalled.
196         return !(overlayPackageState.isSystem() || overlayPackage.isSignedWithPlatformKey());
197     }
198 
199     /**
200      * Retrieves a bitmask for idmap2 that represents the policies the overlay fulfills.
201      */
calculateFulfilledPolicies(@onNull final AndroidPackage targetPackage, @NonNull PackageState overlayPackageState, @NonNull final AndroidPackage overlayPackage, @UserIdInt int userId)202     private int calculateFulfilledPolicies(@NonNull final AndroidPackage targetPackage,
203             @NonNull PackageState overlayPackageState, @NonNull final AndroidPackage overlayPackage,
204             @UserIdInt int userId)  {
205         int fulfilledPolicies = OverlayablePolicy.PUBLIC;
206 
207         // Overlay matches target signature
208         if (mPackageManager.signaturesMatching(targetPackage.getPackageName(),
209                 overlayPackage.getPackageName(), userId)) {
210             fulfilledPolicies |= OverlayablePolicy.SIGNATURE;
211         }
212 
213         // Overlay matches actor signature
214         if (matchesActorSignature(targetPackage, overlayPackage, userId)) {
215             fulfilledPolicies |= OverlayablePolicy.ACTOR_SIGNATURE;
216         }
217 
218         // If SystemConfig defines 'overlay-config-signature' package, given that
219         // this package is vetted by OverlayManagerService that it's a
220         // preinstalled package, check if overlay matches its signature.
221         if (!TextUtils.isEmpty(mConfigSignaturePackage)
222                 && mPackageManager.signaturesMatching(mConfigSignaturePackage,
223                                                            overlayPackage.getPackageName(),
224                                                            userId)) {
225             fulfilledPolicies |= OverlayablePolicy.CONFIG_SIGNATURE;
226         }
227 
228         // Vendor partition (/vendor)
229         if (overlayPackageState.isVendor()) {
230             return fulfilledPolicies | OverlayablePolicy.VENDOR_PARTITION;
231         }
232 
233         // Product partition (/product)
234         if (overlayPackageState.isProduct()) {
235             return fulfilledPolicies | OverlayablePolicy.PRODUCT_PARTITION;
236         }
237 
238         // Odm partition (/odm)
239         if (overlayPackageState.isOdm()) {
240             return fulfilledPolicies | OverlayablePolicy.ODM_PARTITION;
241         }
242 
243         // Oem partition (/oem)
244         if (overlayPackageState.isOem()) {
245             return fulfilledPolicies | OverlayablePolicy.OEM_PARTITION;
246         }
247 
248         // System_ext partition (/system_ext) is considered as system
249         // Check this last since every partition except for data is scanned as system in the PMS.
250         if (overlayPackageState.isSystem() || overlayPackageState.isSystemExt()) {
251             return fulfilledPolicies | OverlayablePolicy.SYSTEM_PARTITION;
252         }
253 
254         return fulfilledPolicies;
255     }
256 
matchesActorSignature(@onNull AndroidPackage targetPackage, @NonNull AndroidPackage overlayPackage, int userId)257     private boolean matchesActorSignature(@NonNull AndroidPackage targetPackage,
258             @NonNull AndroidPackage overlayPackage, int userId) {
259         String targetOverlayableName = overlayPackage.getOverlayTargetOverlayableName();
260         if (targetOverlayableName != null && !mPackageManager.getNamedActors().isEmpty()) {
261             try {
262                 OverlayableInfo overlayableInfo = mPackageManager.getOverlayableForTarget(
263                         targetPackage.getPackageName(), targetOverlayableName, userId);
264                 if (overlayableInfo != null && overlayableInfo.actor != null) {
265                     String actorPackageName = OverlayActorEnforcer.getPackageNameForActor(
266                             overlayableInfo.actor, mPackageManager.getNamedActors()).first;
267                     if (mPackageManager.signaturesMatching(actorPackageName,
268                             overlayPackage.getPackageName(), userId)) {
269                         return true;
270                     }
271                 }
272             } catch (IOException ignored) {
273             }
274         }
275 
276         return false;
277     }
278 }
279