• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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