1 /* 2 * Copyright (C) 2019 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.integrity; 18 19 import static android.content.Intent.ACTION_PACKAGE_NEEDS_INTEGRITY_VERIFICATION; 20 import static android.content.Intent.EXTRA_LONG_VERSION_CODE; 21 import static android.content.Intent.EXTRA_ORIGINATING_UID; 22 import static android.content.Intent.EXTRA_PACKAGE_NAME; 23 import static android.content.integrity.AppIntegrityManager.EXTRA_STATUS; 24 import static android.content.integrity.AppIntegrityManager.STATUS_FAILURE; 25 import static android.content.integrity.AppIntegrityManager.STATUS_SUCCESS; 26 import static android.content.integrity.InstallerAllowedByManifestFormula.INSTALLER_CERTIFICATE_NOT_EVALUATED; 27 import static android.content.integrity.IntegrityUtils.getHexDigest; 28 import static android.content.pm.PackageManager.EXTRA_VERIFICATION_ID; 29 30 import android.annotation.BinderThread; 31 import android.annotation.NonNull; 32 import android.annotation.Nullable; 33 import android.content.BroadcastReceiver; 34 import android.content.Context; 35 import android.content.Intent; 36 import android.content.IntentFilter; 37 import android.content.IntentSender; 38 import android.content.integrity.AppInstallMetadata; 39 import android.content.integrity.IAppIntegrityManager; 40 import android.content.integrity.Rule; 41 import android.content.pm.PackageInfo; 42 import android.content.pm.PackageManager; 43 import android.content.pm.PackageManagerInternal; 44 import android.content.pm.ParceledListSlice; 45 import android.content.pm.Signature; 46 import android.content.pm.SigningDetails; 47 import android.content.pm.parsing.result.ParseResult; 48 import android.content.pm.parsing.result.ParseTypeImpl; 49 import android.net.Uri; 50 import android.os.Binder; 51 import android.os.Bundle; 52 import android.os.Handler; 53 import android.os.HandlerThread; 54 import android.provider.Settings; 55 import android.util.Pair; 56 import android.util.Slog; 57 import android.util.apk.SourceStampVerificationResult; 58 import android.util.apk.SourceStampVerifier; 59 60 import com.android.internal.R; 61 import com.android.internal.annotations.VisibleForTesting; 62 import com.android.internal.util.ArrayUtils; 63 import com.android.internal.util.FrameworkStatsLog; 64 import com.android.server.LocalServices; 65 import com.android.server.integrity.engine.RuleEvaluationEngine; 66 import com.android.server.integrity.model.IntegrityCheckResult; 67 import com.android.server.integrity.model.RuleMetadata; 68 import com.android.server.pm.PackageManagerServiceUtils; 69 import com.android.server.pm.parsing.PackageParser2; 70 import com.android.server.pm.pkg.parsing.ParsingPackageUtils; 71 72 import java.io.ByteArrayInputStream; 73 import java.io.File; 74 import java.io.IOException; 75 import java.io.InputStream; 76 import java.nio.charset.StandardCharsets; 77 import java.nio.file.Files; 78 import java.nio.file.Path; 79 import java.security.MessageDigest; 80 import java.security.NoSuchAlgorithmException; 81 import java.security.cert.CertificateEncodingException; 82 import java.security.cert.CertificateException; 83 import java.security.cert.CertificateFactory; 84 import java.security.cert.X509Certificate; 85 import java.util.ArrayList; 86 import java.util.Arrays; 87 import java.util.Collections; 88 import java.util.HashMap; 89 import java.util.HashSet; 90 import java.util.List; 91 import java.util.Map; 92 import java.util.Set; 93 import java.util.function.Supplier; 94 import java.util.stream.Collectors; 95 import java.util.stream.Stream; 96 97 /** Implementation of {@link AppIntegrityManagerService}. */ 98 public class AppIntegrityManagerServiceImpl extends IAppIntegrityManager.Stub { 99 /** 100 * This string will be used as the "installer" for formula evaluation when the app's installer 101 * cannot be determined. 102 * 103 * <p>This may happen for various reasons. e.g., the installing app's package name may not match 104 * its UID. 105 */ 106 private static final String UNKNOWN_INSTALLER = ""; 107 /** 108 * This string will be used as the "installer" for formula evaluation when the app is being 109 * installed via ADB. 110 */ 111 public static final String ADB_INSTALLER = "adb"; 112 113 private static final String TAG = "AppIntegrityManagerServiceImpl"; 114 115 private static final String PACKAGE_MIME_TYPE = "application/vnd.android.package-archive"; 116 private static final String BASE_APK_FILE = "base.apk"; 117 private static final String ALLOWED_INSTALLERS_METADATA_NAME = "allowed-installers"; 118 private static final String ALLOWED_INSTALLER_DELIMITER = ","; 119 private static final String INSTALLER_PACKAGE_CERT_DELIMITER = "\\|"; 120 121 public static final boolean DEBUG_INTEGRITY_COMPONENT = false; 122 123 private static final Set<String> PACKAGE_INSTALLER = 124 new HashSet<>( 125 Arrays.asList( 126 "com.google.android.packageinstaller", "com.android.packageinstaller")); 127 128 // Access to files inside mRulesDir is protected by mRulesLock; 129 private final Context mContext; 130 private final Handler mHandler; 131 private final PackageManagerInternal mPackageManagerInternal; 132 private final Supplier<PackageParser2> mParserSupplier; 133 private final RuleEvaluationEngine mEvaluationEngine; 134 private final IntegrityFileManager mIntegrityFileManager; 135 136 /** Create an instance of {@link AppIntegrityManagerServiceImpl}. */ create(Context context)137 public static AppIntegrityManagerServiceImpl create(Context context) { 138 HandlerThread handlerThread = new HandlerThread("AppIntegrityManagerServiceHandler"); 139 handlerThread.start(); 140 141 return new AppIntegrityManagerServiceImpl( 142 context, 143 LocalServices.getService(PackageManagerInternal.class), 144 PackageParser2::forParsingFileWithDefaults, 145 RuleEvaluationEngine.getRuleEvaluationEngine(), 146 IntegrityFileManager.getInstance(), 147 handlerThread.getThreadHandler()); 148 } 149 150 @VisibleForTesting AppIntegrityManagerServiceImpl( Context context, PackageManagerInternal packageManagerInternal, Supplier<PackageParser2> parserSupplier, RuleEvaluationEngine evaluationEngine, IntegrityFileManager integrityFileManager, Handler handler)151 AppIntegrityManagerServiceImpl( 152 Context context, 153 PackageManagerInternal packageManagerInternal, 154 Supplier<PackageParser2> parserSupplier, 155 RuleEvaluationEngine evaluationEngine, 156 IntegrityFileManager integrityFileManager, 157 Handler handler) { 158 mContext = context; 159 mPackageManagerInternal = packageManagerInternal; 160 mParserSupplier = parserSupplier; 161 mEvaluationEngine = evaluationEngine; 162 mIntegrityFileManager = integrityFileManager; 163 mHandler = handler; 164 165 IntentFilter integrityVerificationFilter = new IntentFilter(); 166 integrityVerificationFilter.addAction(ACTION_PACKAGE_NEEDS_INTEGRITY_VERIFICATION); 167 try { 168 integrityVerificationFilter.addDataType(PACKAGE_MIME_TYPE); 169 } catch (IntentFilter.MalformedMimeTypeException e) { 170 throw new RuntimeException("Mime type malformed: should never happen.", e); 171 } 172 173 mContext.registerReceiver( 174 new BroadcastReceiver() { 175 @Override 176 public void onReceive(Context context, Intent intent) { 177 if (!ACTION_PACKAGE_NEEDS_INTEGRITY_VERIFICATION.equals( 178 intent.getAction())) { 179 return; 180 } 181 mHandler.post(() -> handleIntegrityVerification(intent)); 182 } 183 }, 184 integrityVerificationFilter, 185 /* broadcastPermission= */ null, 186 mHandler); 187 } 188 189 @Override 190 @BinderThread updateRuleSet( String version, ParceledListSlice<Rule> rules, IntentSender statusReceiver)191 public void updateRuleSet( 192 String version, ParceledListSlice<Rule> rules, IntentSender statusReceiver) { 193 String ruleProvider = getCallerPackageNameOrThrow(Binder.getCallingUid()); 194 if (DEBUG_INTEGRITY_COMPONENT) { 195 Slog.i(TAG, String.format("Calling rule provider name is: %s.", ruleProvider)); 196 } 197 198 mHandler.post( 199 () -> { 200 boolean success = true; 201 try { 202 mIntegrityFileManager.writeRules(version, ruleProvider, rules.getList()); 203 } catch (Exception e) { 204 Slog.e(TAG, "Error writing rules.", e); 205 success = false; 206 } 207 208 if (DEBUG_INTEGRITY_COMPONENT) { 209 Slog.i( 210 TAG, 211 String.format( 212 "Successfully pushed rule set to version '%s' from '%s'", 213 version, ruleProvider)); 214 } 215 216 FrameworkStatsLog.write( 217 FrameworkStatsLog.INTEGRITY_RULES_PUSHED, 218 success, 219 ruleProvider, 220 version); 221 222 Intent intent = new Intent(); 223 intent.putExtra(EXTRA_STATUS, success ? STATUS_SUCCESS : STATUS_FAILURE); 224 try { 225 statusReceiver.sendIntent( 226 mContext, 227 /* code= */ 0, 228 intent, 229 /* onFinished= */ null, 230 /* handler= */ null); 231 } catch (Exception e) { 232 Slog.e(TAG, "Error sending status feedback.", e); 233 } 234 }); 235 } 236 237 @Override 238 @BinderThread getCurrentRuleSetVersion()239 public String getCurrentRuleSetVersion() { 240 getCallerPackageNameOrThrow(Binder.getCallingUid()); 241 242 RuleMetadata ruleMetadata = mIntegrityFileManager.readMetadata(); 243 return (ruleMetadata != null && ruleMetadata.getVersion() != null) 244 ? ruleMetadata.getVersion() 245 : ""; 246 } 247 248 @Override 249 @BinderThread getCurrentRuleSetProvider()250 public String getCurrentRuleSetProvider() { 251 getCallerPackageNameOrThrow(Binder.getCallingUid()); 252 253 RuleMetadata ruleMetadata = mIntegrityFileManager.readMetadata(); 254 return (ruleMetadata != null && ruleMetadata.getRuleProvider() != null) 255 ? ruleMetadata.getRuleProvider() 256 : ""; 257 } 258 259 @Override getCurrentRules()260 public ParceledListSlice<Rule> getCurrentRules() { 261 List<Rule> rules = Collections.emptyList(); 262 try { 263 rules = mIntegrityFileManager.readRules(/* appInstallMetadata= */ null); 264 } catch (Exception e) { 265 Slog.e(TAG, "Error getting current rules", e); 266 } 267 return new ParceledListSlice<>(rules); 268 } 269 270 @Override getWhitelistedRuleProviders()271 public List<String> getWhitelistedRuleProviders() { 272 return getAllowedRuleProviderSystemApps(); 273 } 274 handleIntegrityVerification(Intent intent)275 private void handleIntegrityVerification(Intent intent) { 276 int verificationId = intent.getIntExtra(EXTRA_VERIFICATION_ID, -1); 277 278 try { 279 if (DEBUG_INTEGRITY_COMPONENT) { 280 Slog.d(TAG, "Received integrity verification intent " + intent.toString()); 281 Slog.d(TAG, "Extras " + intent.getExtras()); 282 } 283 284 String installerPackageName = getInstallerPackageName(intent); 285 286 // Skip integrity verification if the verifier is doing the install. 287 if (!integrityCheckIncludesRuleProvider() && isRuleProvider(installerPackageName)) { 288 if (DEBUG_INTEGRITY_COMPONENT) { 289 Slog.i(TAG, "Verifier doing the install. Skipping integrity check."); 290 } 291 mPackageManagerInternal.setIntegrityVerificationResult( 292 verificationId, PackageManagerInternal.INTEGRITY_VERIFICATION_ALLOW); 293 return; 294 } 295 296 String packageName = intent.getStringExtra(EXTRA_PACKAGE_NAME); 297 298 Pair<SigningDetails, Bundle> packageSigningAndMetadata = 299 getPackageSigningAndMetadata(intent.getData()); 300 if (packageSigningAndMetadata == null) { 301 Slog.w(TAG, "Cannot parse package " + packageName); 302 // We can't parse the package. 303 mPackageManagerInternal.setIntegrityVerificationResult( 304 verificationId, PackageManagerInternal.INTEGRITY_VERIFICATION_ALLOW); 305 return; 306 } 307 308 var signingDetails = packageSigningAndMetadata.first; 309 List<String> appCertificates = getCertificateFingerprint(packageName, signingDetails); 310 List<String> appCertificateLineage = getCertificateLineage(packageName, signingDetails); 311 List<String> installerCertificates = 312 getInstallerCertificateFingerprint(installerPackageName); 313 314 AppInstallMetadata.Builder builder = new AppInstallMetadata.Builder(); 315 316 builder.setPackageName(getPackageNameNormalized(packageName)); 317 builder.setAppCertificates(appCertificates); 318 builder.setAppCertificateLineage(appCertificateLineage); 319 builder.setVersionCode(intent.getLongExtra(EXTRA_LONG_VERSION_CODE, -1)); 320 builder.setInstallerName(getPackageNameNormalized(installerPackageName)); 321 builder.setInstallerCertificates(installerCertificates); 322 builder.setIsPreInstalled(isSystemApp(packageName)); 323 324 Map<String, String> allowedInstallers = 325 getAllowedInstallers(packageSigningAndMetadata.second); 326 builder.setAllowedInstallersAndCert(allowedInstallers); 327 extractSourceStamp(intent.getData(), builder); 328 329 AppInstallMetadata appInstallMetadata = builder.build(); 330 331 if (DEBUG_INTEGRITY_COMPONENT) { 332 Slog.i( 333 TAG, 334 "To be verified: " 335 + appInstallMetadata 336 + " installers " 337 + allowedInstallers); 338 } 339 IntegrityCheckResult result = mEvaluationEngine.evaluate(appInstallMetadata); 340 if (!result.getMatchedRules().isEmpty() || DEBUG_INTEGRITY_COMPONENT) { 341 Slog.i( 342 TAG, 343 String.format( 344 "Integrity check of %s result: %s due to %s", 345 packageName, result.getEffect(), result.getMatchedRules())); 346 } 347 348 FrameworkStatsLog.write( 349 FrameworkStatsLog.INTEGRITY_CHECK_RESULT_REPORTED, 350 packageName, 351 appCertificates.toString(), 352 appInstallMetadata.getVersionCode(), 353 installerPackageName, 354 result.getLoggingResponse(), 355 result.isCausedByAppCertRule(), 356 result.isCausedByInstallerRule()); 357 mPackageManagerInternal.setIntegrityVerificationResult( 358 verificationId, 359 result.getEffect() == IntegrityCheckResult.Effect.ALLOW 360 ? PackageManagerInternal.INTEGRITY_VERIFICATION_ALLOW 361 : PackageManagerInternal.INTEGRITY_VERIFICATION_REJECT); 362 } catch (IllegalArgumentException e) { 363 // This exception indicates something is wrong with the input passed by package manager. 364 // e.g., someone trying to trick the system. We block installs in this case. 365 Slog.e(TAG, "Invalid input to integrity verification", e); 366 mPackageManagerInternal.setIntegrityVerificationResult( 367 verificationId, PackageManagerInternal.INTEGRITY_VERIFICATION_REJECT); 368 } catch (Exception e) { 369 // Other exceptions indicate an error within the integrity component implementation and 370 // we allow them. 371 Slog.e(TAG, "Error handling integrity verification", e); 372 mPackageManagerInternal.setIntegrityVerificationResult( 373 verificationId, PackageManagerInternal.INTEGRITY_VERIFICATION_ALLOW); 374 } 375 } 376 377 /** 378 * Verify the UID and return the installer package name. 379 * 380 * @return the package name of the installer, or null if it cannot be determined or it is 381 * installed via adb. 382 */ 383 @Nullable getInstallerPackageName(Intent intent)384 private String getInstallerPackageName(Intent intent) { 385 String installer = 386 intent.getStringExtra(PackageManager.EXTRA_VERIFICATION_INSTALLER_PACKAGE); 387 if (PackageManagerServiceUtils.isInstalledByAdb(installer)) { 388 return ADB_INSTALLER; 389 } 390 int installerUid = intent.getIntExtra(PackageManager.EXTRA_VERIFICATION_INSTALLER_UID, -1); 391 if (installerUid < 0) { 392 Slog.e( 393 TAG, 394 "Installer cannot be determined: installer: " 395 + installer 396 + " installer UID: " 397 + installerUid); 398 return UNKNOWN_INSTALLER; 399 } 400 401 // Verify that the installer UID actually contains the package. Note that comparing UIDs 402 // is not safe since context's uid can change in different settings; e.g. Android Auto. 403 if (!getPackageListForUid(installerUid).contains(installer)) { 404 return UNKNOWN_INSTALLER; 405 } 406 407 // At this time we can trust "installer". 408 409 // A common way for apps to install packages is to send an intent to PackageInstaller. In 410 // that case, the installer will always show up as PackageInstaller which is not what we 411 // want. 412 if (PACKAGE_INSTALLER.contains(installer)) { 413 int originatingUid = intent.getIntExtra(EXTRA_ORIGINATING_UID, -1); 414 if (originatingUid < 0) { 415 Slog.e(TAG, "Installer is package installer but originating UID not found."); 416 return UNKNOWN_INSTALLER; 417 } 418 List<String> installerPackages = getPackageListForUid(originatingUid); 419 if (installerPackages.isEmpty()) { 420 Slog.e(TAG, "No package found associated with originating UID " + originatingUid); 421 return UNKNOWN_INSTALLER; 422 } 423 // In the case of multiple package sharing a UID, we just return the first one. 424 return installerPackages.get(0); 425 } 426 427 return installer; 428 } 429 430 /** We will use the SHA256 digest of a package name if it is more than 32 bytes long. */ getPackageNameNormalized(String packageName)431 private String getPackageNameNormalized(String packageName) { 432 if (packageName.length() <= 32) { 433 return packageName; 434 } 435 436 try { 437 MessageDigest messageDigest = MessageDigest.getInstance("SHA-256"); 438 byte[] hashBytes = messageDigest.digest(packageName.getBytes(StandardCharsets.UTF_8)); 439 return getHexDigest(hashBytes); 440 } catch (NoSuchAlgorithmException e) { 441 throw new RuntimeException("SHA-256 algorithm not found", e); 442 } 443 } 444 getInstallerCertificateFingerprint(String installer)445 private List<String> getInstallerCertificateFingerprint(String installer) { 446 if (installer.equals(ADB_INSTALLER) || installer.equals(UNKNOWN_INSTALLER)) { 447 return Collections.emptyList(); 448 } 449 var installerPkg = mPackageManagerInternal.getPackage(installer); 450 if (installerPkg == null) { 451 Slog.w(TAG, "Installer package " + installer + " not found."); 452 return Collections.emptyList(); 453 } 454 return getCertificateFingerprint(installerPkg.getPackageName(), 455 installerPkg.getSigningDetails()); 456 } 457 getCertificateFingerprint(@onNull String packageName, @NonNull SigningDetails signingDetails)458 private List<String> getCertificateFingerprint(@NonNull String packageName, 459 @NonNull SigningDetails signingDetails) { 460 ArrayList<String> certificateFingerprints = new ArrayList(); 461 for (Signature signature : getSignatures(packageName, signingDetails)) { 462 certificateFingerprints.add(getFingerprint(signature)); 463 } 464 return certificateFingerprints; 465 } 466 getCertificateLineage(@onNull String packageName, @NonNull SigningDetails signingDetails)467 private List<String> getCertificateLineage(@NonNull String packageName, 468 @NonNull SigningDetails signingDetails) { 469 ArrayList<String> certificateLineage = new ArrayList(); 470 for (Signature signature : getSignatureLineage(packageName, signingDetails)) { 471 certificateLineage.add(getFingerprint(signature)); 472 } 473 return certificateLineage; 474 } 475 476 /** Get the allowed installers and their associated certificate hashes from <meta-data> tag. */ getAllowedInstallers(@ullable Bundle metaData)477 private Map<String, String> getAllowedInstallers(@Nullable Bundle metaData) { 478 Map<String, String> packageCertMap = new HashMap<>(); 479 if (metaData != null) { 480 String allowedInstallers = metaData.getString(ALLOWED_INSTALLERS_METADATA_NAME); 481 if (allowedInstallers != null) { 482 // parse the metadata for certs. 483 String[] installerCertPairs = allowedInstallers.split(ALLOWED_INSTALLER_DELIMITER); 484 for (String packageCertPair : installerCertPairs) { 485 String[] packageAndCert = 486 packageCertPair.split(INSTALLER_PACKAGE_CERT_DELIMITER); 487 if (packageAndCert.length == 2) { 488 String packageName = getPackageNameNormalized(packageAndCert[0]); 489 String cert = packageAndCert[1]; 490 packageCertMap.put(packageName, cert); 491 } else if (packageAndCert.length == 1) { 492 packageCertMap.put( 493 getPackageNameNormalized(packageAndCert[0]), 494 INSTALLER_CERTIFICATE_NOT_EVALUATED); 495 } 496 } 497 } 498 } 499 500 return packageCertMap; 501 } 502 503 /** Extract the source stamp embedded in the APK, if present. */ extractSourceStamp(Uri dataUri, AppInstallMetadata.Builder appInstallMetadata)504 private void extractSourceStamp(Uri dataUri, AppInstallMetadata.Builder appInstallMetadata) { 505 File installationPath = getInstallationPath(dataUri); 506 if (installationPath == null) { 507 throw new IllegalArgumentException("Installation path is null, package not found"); 508 } 509 510 SourceStampVerificationResult sourceStampVerificationResult; 511 if (installationPath.isDirectory()) { 512 try (Stream<Path> filesList = Files.list(installationPath.toPath())) { 513 List<String> apkFiles = 514 filesList 515 .map(path -> path.toAbsolutePath().toString()) 516 .collect(Collectors.toList()); 517 sourceStampVerificationResult = SourceStampVerifier.verify(apkFiles); 518 } catch (IOException e) { 519 throw new IllegalArgumentException("Could not read APK directory"); 520 } 521 } else { 522 sourceStampVerificationResult = 523 SourceStampVerifier.verify(installationPath.getAbsolutePath()); 524 } 525 526 appInstallMetadata.setIsStampPresent(sourceStampVerificationResult.isPresent()); 527 appInstallMetadata.setIsStampVerified(sourceStampVerificationResult.isVerified()); 528 // A verified stamp is set to be trusted. 529 appInstallMetadata.setIsStampTrusted(sourceStampVerificationResult.isVerified()); 530 if (sourceStampVerificationResult.isVerified()) { 531 X509Certificate sourceStampCertificate = 532 (X509Certificate) sourceStampVerificationResult.getCertificate(); 533 // Sets source stamp certificate digest. 534 try { 535 MessageDigest digest = MessageDigest.getInstance("SHA-256"); 536 byte[] certificateDigest = digest.digest(sourceStampCertificate.getEncoded()); 537 appInstallMetadata.setStampCertificateHash(getHexDigest(certificateDigest)); 538 } catch (NoSuchAlgorithmException | CertificateEncodingException e) { 539 throw new IllegalArgumentException( 540 "Error computing source stamp certificate digest", e); 541 } 542 } 543 } 544 getSignatures(@onNull String packageName, @NonNull SigningDetails signingDetails)545 private static Signature[] getSignatures(@NonNull String packageName, 546 @NonNull SigningDetails signingDetails) { 547 Signature[] signatures = signingDetails.getSignatures(); 548 if (signatures == null || signatures.length < 1) { 549 throw new IllegalArgumentException("Package signature not found in " + packageName); 550 } 551 552 // We are only interested in evaluating the active signatures. 553 return signatures; 554 } 555 getSignatureLineage(@onNull String packageName, @NonNull SigningDetails signingDetails)556 private static Signature[] getSignatureLineage(@NonNull String packageName, 557 @NonNull SigningDetails signingDetails) { 558 // Obtain the active signatures of the package. 559 Signature[] signatureLineage = getSignatures(packageName, signingDetails); 560 561 var pastSignatures = signingDetails.getPastSigningCertificates(); 562 // Obtain the past signatures of the package. 563 if (signatureLineage.length == 1 && !ArrayUtils.isEmpty(pastSignatures)) { 564 // Merge the signatures and return. 565 Signature[] allSignatures = 566 new Signature[signatureLineage.length + pastSignatures.length]; 567 int i; 568 for (i = 0; i < signatureLineage.length; i++) { 569 allSignatures[i] = signatureLineage[i]; 570 } 571 for (int j = 0; j < pastSignatures.length; j++) { 572 allSignatures[i] = pastSignatures[j]; 573 i++; 574 } 575 signatureLineage = allSignatures; 576 } 577 578 return signatureLineage; 579 } 580 getFingerprint(Signature cert)581 private static String getFingerprint(Signature cert) { 582 InputStream input = new ByteArrayInputStream(cert.toByteArray()); 583 584 CertificateFactory factory; 585 try { 586 factory = CertificateFactory.getInstance("X509"); 587 } catch (CertificateException e) { 588 throw new RuntimeException("Error getting CertificateFactory", e); 589 } 590 X509Certificate certificate = null; 591 try { 592 if (factory != null) { 593 certificate = (X509Certificate) factory.generateCertificate(input); 594 } 595 } catch (CertificateException e) { 596 throw new RuntimeException("Error getting X509Certificate", e); 597 } 598 599 if (certificate == null) { 600 throw new RuntimeException("X509 Certificate not found"); 601 } 602 603 try { 604 MessageDigest digest = MessageDigest.getInstance("SHA-256"); 605 byte[] publicKey = digest.digest(certificate.getEncoded()); 606 return getHexDigest(publicKey); 607 } catch (NoSuchAlgorithmException | CertificateEncodingException e) { 608 throw new IllegalArgumentException("Error error computing fingerprint", e); 609 } 610 } 611 612 @Nullable getPackageSigningAndMetadata(Uri dataUri)613 private Pair<SigningDetails, Bundle> getPackageSigningAndMetadata(Uri dataUri) { 614 File installationPath = getInstallationPath(dataUri); 615 if (installationPath == null) { 616 throw new IllegalArgumentException("Installation path is null, package not found"); 617 } 618 619 try (PackageParser2 parser = mParserSupplier.get()) { 620 var pkg = parser.parsePackage(installationPath, 0, false); 621 // APK signatures is already verified elsewhere in PackageManager. We do not need to 622 // verify it again since it could cause a timeout for large APKs. 623 final ParseTypeImpl input = ParseTypeImpl.forDefaultParsing(); 624 final ParseResult<SigningDetails> result = ParsingPackageUtils.getSigningDetails( 625 input, pkg, /* skipVerify= */ true); 626 if (result.isError()) { 627 Slog.w(TAG, result.getErrorMessage(), result.getException()); 628 return null; 629 } 630 return Pair.create(result.getResult(), pkg.getMetaData()); 631 } catch (Exception e) { 632 Slog.w(TAG, "Exception reading " + dataUri, e); 633 return null; 634 } 635 } 636 getMultiApkInfo(File multiApkDirectory)637 private PackageInfo getMultiApkInfo(File multiApkDirectory) { 638 // The base apk will normally be called base.apk 639 File baseFile = new File(multiApkDirectory, BASE_APK_FILE); 640 PackageInfo basePackageInfo = 641 mContext.getPackageManager() 642 .getPackageArchiveInfo( 643 baseFile.getAbsolutePath(), 644 PackageManager.GET_SIGNING_CERTIFICATES 645 | PackageManager.GET_META_DATA); 646 647 if (basePackageInfo == null) { 648 for (File apkFile : multiApkDirectory.listFiles()) { 649 if (apkFile.isDirectory()) { 650 continue; 651 } 652 653 // If we didn't find a base.apk, then try to parse each apk until we find the one 654 // that succeeds. 655 try { 656 basePackageInfo = 657 mContext.getPackageManager() 658 .getPackageArchiveInfo( 659 apkFile.getAbsolutePath(), 660 PackageManager.GET_SIGNING_CERTIFICATES 661 | PackageManager.GET_META_DATA); 662 } catch (Exception e) { 663 // Some of the splits may not contain a valid android manifest. It is an 664 // expected exception. We still log it nonetheless but we should keep looking. 665 Slog.w(TAG, "Exception reading " + apkFile, e); 666 } 667 if (basePackageInfo != null) { 668 Slog.i(TAG, "Found package info from " + apkFile); 669 break; 670 } 671 } 672 } 673 674 if (basePackageInfo == null) { 675 throw new IllegalArgumentException( 676 "Base package info cannot be found from installation directory"); 677 } 678 679 return basePackageInfo; 680 } 681 getInstallationPath(Uri dataUri)682 private File getInstallationPath(Uri dataUri) { 683 if (dataUri == null) { 684 throw new IllegalArgumentException("Null data uri"); 685 } 686 687 String scheme = dataUri.getScheme(); 688 if (!"file".equalsIgnoreCase(scheme)) { 689 throw new IllegalArgumentException("Unsupported scheme for " + dataUri); 690 } 691 692 File installationPath = new File(dataUri.getPath()); 693 if (!installationPath.exists()) { 694 throw new IllegalArgumentException("Cannot find file for " + dataUri); 695 } 696 if (!installationPath.canRead()) { 697 throw new IllegalArgumentException("Cannot read file for " + dataUri); 698 } 699 return installationPath; 700 } 701 getCallerPackageNameOrThrow(int callingUid)702 private String getCallerPackageNameOrThrow(int callingUid) { 703 String callerPackageName = getCallingRulePusherPackageName(callingUid); 704 if (callerPackageName == null) { 705 throw new SecurityException( 706 "Only system packages specified in config_integrityRuleProviderPackages are " 707 + "allowed to call this method."); 708 } 709 return callerPackageName; 710 } 711 getCallingRulePusherPackageName(int callingUid)712 private String getCallingRulePusherPackageName(int callingUid) { 713 // Obtain the system apps that are whitelisted in config_integrityRuleProviderPackages. 714 List<String> allowedRuleProviders = getAllowedRuleProviderSystemApps(); 715 if (DEBUG_INTEGRITY_COMPONENT) { 716 Slog.i( 717 TAG, 718 String.format( 719 "Rule provider system app list contains: %s", allowedRuleProviders)); 720 } 721 722 // Identify the package names in the caller list. 723 List<String> callingPackageNames = getPackageListForUid(callingUid); 724 725 // Find the intersection between the allowed and calling packages. Ideally, we will have 726 // at most one package name here. But if we have more, it is fine. 727 List<String> allowedCallingPackages = new ArrayList<>(); 728 for (String packageName : callingPackageNames) { 729 if (allowedRuleProviders.contains(packageName)) { 730 allowedCallingPackages.add(packageName); 731 } 732 } 733 734 return allowedCallingPackages.isEmpty() ? null : allowedCallingPackages.get(0); 735 } 736 isRuleProvider(String installerPackageName)737 private boolean isRuleProvider(String installerPackageName) { 738 for (String ruleProvider : getAllowedRuleProviderSystemApps()) { 739 if (ruleProvider.matches(installerPackageName)) { 740 return true; 741 } 742 } 743 return false; 744 } 745 getAllowedRuleProviderSystemApps()746 private List<String> getAllowedRuleProviderSystemApps() { 747 List<String> integrityRuleProviders = 748 Arrays.asList( 749 mContext.getResources() 750 .getStringArray(R.array.config_integrityRuleProviderPackages)); 751 752 // Filter out the rule provider packages that are not system apps. 753 List<String> systemAppRuleProviders = new ArrayList<>(); 754 for (String ruleProvider : integrityRuleProviders) { 755 if (isSystemApp(ruleProvider)) { 756 systemAppRuleProviders.add(ruleProvider); 757 } 758 } 759 return systemAppRuleProviders; 760 } 761 isSystemApp(String packageName)762 private boolean isSystemApp(String packageName) { 763 try { 764 PackageInfo existingPackageInfo = 765 mContext.getPackageManager().getPackageInfo(packageName, /* flags= */ 0); 766 return existingPackageInfo.applicationInfo != null 767 && existingPackageInfo.applicationInfo.isSystemApp(); 768 } catch (PackageManager.NameNotFoundException e) { 769 return false; 770 } 771 } 772 integrityCheckIncludesRuleProvider()773 private boolean integrityCheckIncludesRuleProvider() { 774 return Settings.Global.getInt( 775 mContext.getContentResolver(), 776 Settings.Global.INTEGRITY_CHECK_INCLUDES_RULE_PROVIDER, 777 0) 778 == 1; 779 } 780 getPackageListForUid(int uid)781 private List<String> getPackageListForUid(int uid) { 782 try { 783 return Arrays.asList(mContext.getPackageManager().getPackagesForUid(uid)); 784 } catch (NullPointerException e) { 785 Slog.w(TAG, String.format("No packages were found for uid: %d", uid)); 786 return List.of(); 787 } 788 } 789 } 790