1 /*
2  * Copyright (C) 2021 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;
18 
19 import static android.content.pm.PackageManager.INSTALL_FAILED_UPDATE_INCOMPATIBLE;
20 import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES;
21 import static android.content.pm.SigningDetails.CapabilityMergeRule.MERGE_RESTRICTED_CAPABILITY;
22 
23 import static com.android.server.pm.PackageManagerService.SCAN_BOOTING;
24 import static com.android.server.pm.PackageManagerService.SCAN_DONT_KILL_APP;
25 
26 import android.content.pm.PackageManager;
27 import android.content.pm.SharedLibraryInfo;
28 import android.content.pm.SigningDetails;
29 import android.os.SystemProperties;
30 import android.util.ArrayMap;
31 import android.util.Log;
32 
33 import com.android.server.pm.parsing.pkg.ParsedPackage;
34 import com.android.server.pm.pkg.AndroidPackage;
35 import com.android.server.pm.pkg.parsing.ParsingPackageUtils;
36 import com.android.server.utils.WatchedLongSparseArray;
37 
38 import java.util.ArrayList;
39 import java.util.List;
40 import java.util.Map;
41 
42 /**
43  * Package scan results and related request details used to reconcile the potential addition of
44  * one or more packages to the system.
45  *
46  * Reconcile will take a set of package details that need to be committed to the system and make
47  * sure that they are valid in the context of the system and the other installing apps. Any
48  * invalid state or app will result in a failed reconciliation and thus whatever operation (such
49  * as install) led to the request.
50  */
51 final class ReconcilePackageUtils {
reconcilePackages( List<InstallRequest> installRequests, Map<String, AndroidPackage> allPackages, Map<String, Settings.VersionInfo> versionInfos, SharedLibrariesImpl sharedLibraries, KeySetManagerService ksms, Settings settings)52     public static List<ReconciledPackage> reconcilePackages(
53             List<InstallRequest> installRequests,
54             Map<String, AndroidPackage> allPackages,
55             Map<String, Settings.VersionInfo> versionInfos,
56             SharedLibrariesImpl sharedLibraries,
57             KeySetManagerService ksms, Settings settings)
58             throws ReconcileFailure {
59         final List<ReconciledPackage> result = new ArrayList<>(installRequests.size());
60 
61         // make a copy of the existing set of packages so we can combine them with incoming packages
62         final ArrayMap<String, AndroidPackage> combinedPackages =
63                 new ArrayMap<>(allPackages.size() + installRequests.size());
64 
65         combinedPackages.putAll(allPackages);
66 
67         final Map<String, WatchedLongSparseArray<SharedLibraryInfo>> incomingSharedLibraries =
68                 new ArrayMap<>();
69 
70         for (InstallRequest installRequest :  installRequests) {
71             installRequest.onReconcileStarted();
72 
73             // add / replace existing with incoming packages
74             combinedPackages.put(installRequest.getScannedPackageSetting().getPackageName(),
75                     installRequest.getParsedPackage());
76 
77             // in the first pass, we'll build up the set of incoming shared libraries
78             final List<SharedLibraryInfo> allowedSharedLibInfos =
79                     sharedLibraries.getAllowedSharedLibInfos(installRequest);
80             if (allowedSharedLibInfos != null) {
81                 for (SharedLibraryInfo info : allowedSharedLibInfos) {
82                     if (!SharedLibraryUtils.addSharedLibraryToPackageVersionMap(
83                             incomingSharedLibraries, info)) {
84                         throw ReconcileFailure.ofInternalError(
85                                 "Shared Library " + info.getName()
86                                         + " is being installed twice in this set!",
87                                 PackageManagerException.INTERNAL_ERROR_SHARED_LIB_INSTALLED_TWICE);
88                     }
89                 }
90             }
91         }
92 
93         for (InstallRequest installRequest : installRequests) {
94             final String installPackageName = installRequest.getParsedPackage().getPackageName();
95             final List<SharedLibraryInfo> allowedSharedLibInfos =
96                     sharedLibraries.getAllowedSharedLibInfos(installRequest);
97 
98             final DeletePackageAction deletePackageAction;
99             // we only want to try to delete for non system apps
100             if (installRequest.isInstallReplace() && !installRequest.isInstallSystem()) {
101                 final boolean killApp = (installRequest.getScanFlags() & SCAN_DONT_KILL_APP) == 0;
102                 final int deleteFlags = PackageManager.DELETE_KEEP_DATA
103                         | (killApp ? 0 : PackageManager.DELETE_DONT_KILL_APP);
104                 deletePackageAction = DeletePackageHelper.mayDeletePackageLocked(
105                         installRequest.getRemovedInfo(),
106                         installRequest.getOriginalPackageSetting(),
107                         installRequest.getDisabledPackageSetting(),
108                         deleteFlags, null /* all users */);
109                 if (deletePackageAction == null) {
110                     throw new ReconcileFailure(
111                             PackageManager.INSTALL_FAILED_REPLACE_COULDNT_DELETE,
112                             "May not delete " + installPackageName + " to replace");
113                 }
114             } else {
115                 deletePackageAction = null;
116             }
117 
118             final int scanFlags = installRequest.getScanFlags();
119             final int parseFlags = installRequest.getParseFlags();
120             final ParsedPackage parsedPackage = installRequest.getParsedPackage();
121             final PackageSetting disabledPkgSetting = installRequest.getDisabledPackageSetting();
122             final PackageSetting lastStaticSharedLibSetting =
123                     installRequest.getStaticSharedLibraryInfo() == null ? null
124                             : sharedLibraries.getStaticSharedLibLatestVersionSetting(
125                                     installRequest);
126             final PackageSetting signatureCheckPs =
127                     lastStaticSharedLibSetting != null
128                             ? lastStaticSharedLibSetting
129                             : installRequest.getScannedPackageSetting();
130             boolean removeAppKeySetData = false;
131             boolean sharedUserSignaturesChanged = false;
132             SigningDetails signingDetails = null;
133             if (parsedPackage != null) {
134                 signingDetails = parsedPackage.getSigningDetails();
135             }
136             SharedUserSetting sharedUserSetting = settings.getSharedUserSettingLPr(
137                     signatureCheckPs);
138             if (ksms.shouldCheckUpgradeKeySetLocked(
139                     signatureCheckPs, sharedUserSetting, scanFlags)) {
140                 if (ksms.checkUpgradeKeySetLocked(signatureCheckPs, parsedPackage)) {
141                     // We just determined the app is signed correctly, so bring
142                     // over the latest parsed certs.
143                 } else {
144                     if ((parseFlags & ParsingPackageUtils.PARSE_IS_SYSTEM_DIR) == 0) {
145                         throw new ReconcileFailure(INSTALL_FAILED_UPDATE_INCOMPATIBLE,
146                                 "Package " + parsedPackage.getPackageName()
147                                         + " upgrade keys do not match the previously installed"
148                                         + " version");
149                     } else {
150                         String msg = "System package " + parsedPackage.getPackageName()
151                                 + " signature changed; retaining data.";
152                         PackageManagerService.reportSettingsProblem(Log.WARN, msg);
153                     }
154                 }
155             } else {
156                 try {
157                     final Settings.VersionInfo versionInfo = versionInfos.get(installPackageName);
158                     final boolean compareCompat = isCompatSignatureUpdateNeeded(versionInfo);
159                     final boolean compareRecover = isRecoverSignatureUpdateNeeded(versionInfo);
160                     final boolean isRollback = installRequest.isRollback();
161                     final boolean compatMatch =
162                             PackageManagerServiceUtils.verifySignatures(signatureCheckPs,
163                                     sharedUserSetting, disabledPkgSetting,
164                                     signingDetails, compareCompat,
165                                     compareRecover, isRollback);
166                     // The new KeySets will be re-added later in the scanning process.
167                     if (compatMatch) {
168                         removeAppKeySetData = true;
169                     }
170 
171                     // if this is is a sharedUser, check to see if the new package is signed by a
172                     // newer
173                     // signing certificate than the existing one, and if so, copy over the new
174                     // details
175                     if (sharedUserSetting != null) {
176                         // Attempt to merge the existing lineage for the shared SigningDetails with
177                         // the lineage of the new package; if the shared SigningDetails are not
178                         // returned this indicates the new package added new signers to the lineage
179                         // and/or changed the capabilities of existing signers in the lineage.
180                         SigningDetails sharedSigningDetails =
181                                 sharedUserSetting.signatures.mSigningDetails;
182                         SigningDetails mergedDetails = sharedSigningDetails.mergeLineageWith(
183                                 signingDetails);
184                         if (mergedDetails != sharedSigningDetails) {
185                             // Use the restricted merge rule with the signing lineages from the
186                             // other packages in the sharedUserId to ensure if any revoke a
187                             // capability from a previous signer then this is reflected in the
188                             // shared lineage.
189                             for (AndroidPackage androidPackage : sharedUserSetting.getPackages()) {
190                                 if (androidPackage.getPackageName() != null
191                                         && !androidPackage.getPackageName().equals(
192                                         parsedPackage.getPackageName())) {
193                                     mergedDetails = mergedDetails.mergeLineageWith(
194                                             androidPackage.getSigningDetails(),
195                                             MERGE_RESTRICTED_CAPABILITY);
196                                 }
197                             }
198                             sharedUserSetting.signatures.mSigningDetails =
199                                     mergedDetails;
200                         }
201                         if (sharedUserSetting.signaturesChanged == null) {
202                             sharedUserSetting.signaturesChanged = Boolean.FALSE;
203                         }
204                     }
205                 } catch (PackageManagerException e) {
206                     if ((parseFlags & ParsingPackageUtils.PARSE_IS_SYSTEM_DIR) == 0) {
207                         throw new ReconcileFailure(e);
208                     }
209                     signingDetails = parsedPackage.getSigningDetails();
210 
211                     // If the system app is part of a shared user we allow that shared user to
212                     // change
213                     // signatures as well as part of an OTA. We still need to verify that the
214                     // signatures
215                     // are consistent within the shared user for a given boot, so only allow
216                     // updating
217                     // the signatures on the first package scanned for the shared user (i.e. if the
218                     // signaturesChanged state hasn't been initialized yet in SharedUserSetting).
219                     if (sharedUserSetting != null) {
220                         if (sharedUserSetting.signaturesChanged != null
221                                 && !PackageManagerServiceUtils.canJoinSharedUserId(
222                                 parsedPackage.getPackageName(), parsedPackage.getSigningDetails(),
223                                 sharedUserSetting,
224                                 PackageManagerServiceUtils.SHARED_USER_ID_JOIN_TYPE_SYSTEM)) {
225                             if (SystemProperties.getInt("ro.product.first_api_level", 0) <= 29) {
226                                 // Mismatched signatures is an error and silently skipping system
227                                 // packages will likely break the device in unforeseen ways.
228                                 // However, we allow the device to boot anyway because, prior to Q,
229                                 // vendors were not expecting the platform to crash in this
230                                 // situation.
231                                 // This WILL be a hard failure on any new API levels after Q.
232                                 throw new ReconcileFailure(
233                                         INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES,
234                                         "Signature mismatch for shared user: "
235                                                 + sharedUserSetting);
236                             } else {
237                                 // Treat mismatched signatures on system packages using a shared
238                                 // UID as
239                                 // fatal for the system overall, rather than just failing to install
240                                 // whichever package happened to be scanned later.
241                                 throw new IllegalStateException(
242                                         "Signature mismatch on system package "
243                                                 + parsedPackage.getPackageName()
244                                                 + " for shared user "
245                                                 + sharedUserSetting);
246                             }
247                         }
248 
249                         sharedUserSignaturesChanged = true;
250                         sharedUserSetting.signatures.mSigningDetails =
251                                 parsedPackage.getSigningDetails();
252                         sharedUserSetting.signaturesChanged = Boolean.TRUE;
253                     }
254                     // File a report about this.
255                     String msg = "System package " + parsedPackage.getPackageName()
256                             + " signature changed; retaining data.";
257                     PackageManagerService.reportSettingsProblem(Log.WARN, msg);
258                 } catch (IllegalArgumentException e) {
259                     // should never happen: certs matched when checking, but not when comparing
260                     // old to new for sharedUser
261                     throw new RuntimeException(
262                             "Signing certificates comparison made on incomparable signing details"
263                                     + " but somehow passed verifySignatures!", e);
264                 }
265             }
266 
267             final ReconciledPackage reconciledPackage =
268                     new ReconciledPackage(installRequests, allPackages, installRequest,
269                             deletePackageAction, allowedSharedLibInfos, signingDetails,
270                             sharedUserSignaturesChanged, removeAppKeySetData);
271 
272             // Check all shared libraries and map to their actual file path.
273             // We only do this here for apps not on a system dir, because those
274             // are the only ones that can fail an install due to this.  We
275             // will take care of the system apps by updating all of their
276             // library paths after the scan is done. Also during the initial
277             // scan don't update any libs as we do this wholesale after all
278             // apps are scanned to avoid dependency based scanning.
279             if ((installRequest.getScanFlags() & SCAN_BOOTING) == 0
280                     && (installRequest.getParseFlags() & ParsingPackageUtils.PARSE_IS_SYSTEM_DIR)
281                     == 0) {
282                 try {
283                     reconciledPackage.mCollectedSharedLibraryInfos =
284                             sharedLibraries.collectSharedLibraryInfos(
285                                     installRequest.getParsedPackage(), combinedPackages,
286                                     incomingSharedLibraries);
287                 } catch (PackageManagerException e) {
288                     throw new ReconcileFailure(e.error, e.getMessage());
289                 }
290             }
291 
292             installRequest.onReconcileFinished();
293             result.add(reconciledPackage);
294         }
295 
296         return result;
297     }
298 
299     /**
300      * If the database version for this type of package (internal storage or
301      * external storage) is less than the version where package signatures
302      * were updated, return true.
303      */
isCompatSignatureUpdateNeeded(Settings.VersionInfo ver)304     public static boolean isCompatSignatureUpdateNeeded(Settings.VersionInfo ver) {
305         return ver.databaseVersion < Settings.DatabaseVersion.SIGNATURE_END_ENTITY;
306     }
307 
isRecoverSignatureUpdateNeeded(Settings.VersionInfo ver)308     public static boolean isRecoverSignatureUpdateNeeded(Settings.VersionInfo ver) {
309         return ver.databaseVersion < Settings.DatabaseVersion.SIGNATURE_MALFORMED_RECOVER;
310     }
311 }
312