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