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