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