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