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