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