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