Parse manifest rules for integrity component.
Bug:143689885,145465546
Test: unit test
Change-Id: I146d7e50c9b6bbc06ce0edb0e845b4f01ffff7a9
diff --git a/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java b/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java
index 4d97683..6c80a88 100644
--- a/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java
+++ b/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java
@@ -25,6 +25,7 @@
import static android.content.integrity.AppIntegrityManager.STATUS_SUCCESS;
import static android.content.pm.PackageManager.EXTRA_VERIFICATION_ID;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.BroadcastReceiver;
import android.content.Context;
@@ -41,6 +42,7 @@
import android.content.pm.Signature;
import android.net.Uri;
import android.os.Binder;
+import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.RemoteException;
@@ -63,6 +65,8 @@
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
+import java.util.HashMap;
+import java.util.Map;
/** Implementation of {@link AppIntegrityManagerService}. */
public class AppIntegrityManagerServiceImpl extends IAppIntegrityManager.Stub {
@@ -72,6 +76,9 @@
private static final char[] HEX_CHARS = "0123456789ABCDEF".toCharArray();
private static final String PACKAGE_INSTALLER = "com.google.android.packageinstaller";
private static final String BASE_APK_FILE = "base.apk";
+ private static final String ALLOWED_INSTALLERS_METADATA_NAME = "allowed-installers";
+ private static final String ALLOWED_INSTALLER_DELIMITER = ",";
+ private static final String INSTALLER_PACKAGE_CERT_DELIMITER = "\\|";
private static final String ADB_INSTALLER = "adb";
private static final String UNKNOWN_INSTALLER = "";
@@ -191,11 +198,21 @@
Slog.i(TAG, "Received integrity verification intent " + intent.toString());
Slog.i(TAG, "Extras " + intent.getExtras());
- AppInstallMetadata.Builder builder = new AppInstallMetadata.Builder();
-
String packageName = intent.getStringExtra(EXTRA_PACKAGE_NAME);
+
+ PackageInfo packageInfo = getPackageArchiveInfo(intent.getData());
+ if (packageInfo == null) {
+ Slog.w(TAG, "Cannot parse package " + packageName);
+ // We can't parse the package.
+ mPackageManagerInternal.setIntegrityVerificationResult(
+ verificationId, PackageManagerInternal.INTEGRITY_VERIFICATION_ALLOW);
+ return;
+ }
+
String installerPackageName = getInstallerPackageName(intent);
- String appCert = getAppCertificateFingerprint(intent.getData());
+ String appCert = getCertificateFingerprint(packageInfo);
+
+ AppInstallMetadata.Builder builder = new AppInstallMetadata.Builder();
builder.setPackageName(getPackageNameNormalized(packageName));
builder.setAppCertificate(appCert == null ? "" : appCert);
@@ -208,7 +225,9 @@
AppInstallMetadata appInstallMetadata = builder.build();
Slog.i(TAG, "To be verified: " + appInstallMetadata);
- IntegrityCheckResult result = mEvaluationEngine.evaluate(appInstallMetadata);
+ IntegrityCheckResult result =
+ mEvaluationEngine.evaluate(
+ appInstallMetadata, getAllowedInstallers(packageInfo));
Slog.i(
TAG,
"Integrity check result: "
@@ -224,14 +243,12 @@
// This exception indicates something is wrong with the input passed by package manager.
// e.g., someone trying to trick the system. We block installs in this case.
Slog.e(TAG, "Invalid input to integrity verification", e);
-
mPackageManagerInternal.setIntegrityVerificationResult(
verificationId, PackageManagerInternal.INTEGRITY_VERIFICATION_REJECT);
} catch (Exception e) {
// Other exceptions indicate an error within the integrity component implementation and
// we allow them.
Slog.e(TAG, "Error handling integrity verification", e);
-
mPackageManagerInternal.setIntegrityVerificationResult(
verificationId, PackageManagerInternal.INTEGRITY_VERIFICATION_ALLOW);
}
@@ -320,8 +337,7 @@
}
}
- private String getAppCertificateFingerprint(Uri dataUri) {
- PackageInfo packageInfo = getPackageArchiveInfo(dataUri);
+ private String getCertificateFingerprint(@NonNull PackageInfo packageInfo) {
return getFingerprint(getSignature(packageInfo));
}
@@ -333,14 +349,50 @@
PackageInfo installerInfo =
mContext.getPackageManager()
.getPackageInfo(installer, PackageManager.GET_SIGNATURES);
- return getFingerprint(getSignature(installerInfo));
+ return getCertificateFingerprint(installerInfo);
} catch (PackageManager.NameNotFoundException e) {
Slog.i(TAG, "Installer package " + installer + " not found.");
return "";
}
}
- private static Signature getSignature(PackageInfo packageInfo) {
+ /** Get the allowed installers and their associated certificate hashes from <meta-data> tag. */
+ private Map<String, String> getAllowedInstallers(@NonNull PackageInfo packageInfo) {
+ Map<String, String> packageCertMap = new HashMap<>();
+ if (packageInfo.applicationInfo != null && packageInfo.applicationInfo.metaData != null) {
+ Bundle metaData = packageInfo.applicationInfo.metaData;
+ String allowedInstallers = metaData.getString(ALLOWED_INSTALLERS_METADATA_NAME);
+ if (allowedInstallers != null) {
+ // parse the metadata for certs.
+ String[] installerCertPairs = allowedInstallers.split(ALLOWED_INSTALLER_DELIMITER);
+ for (String packageCertPair : installerCertPairs) {
+ String[] packageAndCert =
+ packageCertPair.split(INSTALLER_PACKAGE_CERT_DELIMITER);
+ if (packageAndCert.length == 2) {
+ String packageName = packageAndCert[0];
+ String cert = packageAndCert[1];
+ packageCertMap.put(packageName, cert);
+ }
+ }
+ }
+ }
+
+ Slog.i("DEBUG", "allowed installers map " + packageCertMap);
+ return packageCertMap;
+ }
+
+ private boolean getPreInstalled(String packageName) {
+ try {
+ PackageInfo existingPackageInfo =
+ mContext.getPackageManager().getPackageInfo(packageName, 0);
+ return existingPackageInfo.applicationInfo != null
+ && existingPackageInfo.applicationInfo.isSystemApp();
+ } catch (PackageManager.NameNotFoundException e) {
+ return false;
+ }
+ }
+
+ private static Signature getSignature(@NonNull PackageInfo packageInfo) {
if (packageInfo.signatures == null || packageInfo.signatures.length < 1) {
throw new IllegalArgumentException("Package signature not found in " + packageInfo);
}
@@ -402,7 +454,9 @@
packageInfo =
mContext.getPackageManager()
.getPackageArchiveInfo(
- installationPath.getPath(), PackageManager.GET_SIGNATURES);
+ installationPath.getPath(),
+ PackageManager.GET_SIGNATURES
+ | PackageManager.GET_META_DATA);
}
return packageInfo;
} catch (Exception e) {
diff --git a/services/core/java/com/android/server/integrity/engine/RuleEvaluationEngine.java b/services/core/java/com/android/server/integrity/engine/RuleEvaluationEngine.java
index 1933679..0ea6efc 100644
--- a/services/core/java/com/android/server/integrity/engine/RuleEvaluationEngine.java
+++ b/services/core/java/com/android/server/integrity/engine/RuleEvaluationEngine.java
@@ -25,6 +25,7 @@
import java.util.ArrayList;
import java.util.List;
+import java.util.Map;
/**
* The engine used to evaluate rules against app installs.
@@ -60,7 +61,8 @@
* against.
* @return result of the integrity check
*/
- public IntegrityCheckResult evaluate(AppInstallMetadata appInstallMetadata) {
+ public IntegrityCheckResult evaluate(
+ AppInstallMetadata appInstallMetadata, Map<String, String> allowedInstallers) {
List<Rule> rules = loadRules(appInstallMetadata);
return RuleEvaluator.evaluateRules(rules, appInstallMetadata);
}
diff --git a/services/tests/servicestests/assets/AppIntegrityManagerServiceImplTest/test.apk b/services/tests/servicestests/assets/AppIntegrityManagerServiceImplTest/test.apk
index e4c4229..6345c98 100644
--- a/services/tests/servicestests/assets/AppIntegrityManagerServiceImplTest/test.apk
+++ b/services/tests/servicestests/assets/AppIntegrityManagerServiceImplTest/test.apk
Binary files differ
diff --git a/services/tests/servicestests/src/com/android/server/integrity/AppIntegrityManagerServiceImplTest.java b/services/tests/servicestests/src/com/android/server/integrity/AppIntegrityManagerServiceImplTest.java
index 222dac9..a2376a6 100644
--- a/services/tests/servicestests/src/com/android/server/integrity/AppIntegrityManagerServiceImplTest.java
+++ b/services/tests/servicestests/src/com/android/server/integrity/AppIntegrityManagerServiceImplTest.java
@@ -79,7 +79,9 @@
import java.nio.file.Files;
import java.nio.file.StandardCopyOption;
import java.util.Arrays;
+import java.util.HashMap;
import java.util.List;
+import java.util.Map;
/** Unit test for {@link com.android.server.integrity.AppIntegrityManagerServiceImpl} */
@RunWith(AndroidJUnit4.class)
@@ -288,21 +290,30 @@
verify(mMockContext)
.registerReceiver(broadcastReceiverCaptor.capture(), any(), any(), any());
Intent intent = makeVerificationIntent();
- when(mRuleEvaluationEngine.evaluate(any())).thenReturn(IntegrityCheckResult.allow());
+ when(mRuleEvaluationEngine.evaluate(any(), any())).thenReturn(IntegrityCheckResult.allow());
broadcastReceiverCaptor.getValue().onReceive(mMockContext, intent);
runJobInHandler();
ArgumentCaptor<AppInstallMetadata> metadataCaptor =
ArgumentCaptor.forClass(AppInstallMetadata.class);
- verify(mRuleEvaluationEngine).evaluate(metadataCaptor.capture());
+ Map<String, String> allowedInstallers = new HashMap<>();
+ ArgumentCaptor<Map<String, String>> allowedInstallersCaptor =
+ ArgumentCaptor.forClass(allowedInstallers.getClass());
+ verify(mRuleEvaluationEngine)
+ .evaluate(metadataCaptor.capture(), allowedInstallersCaptor.capture());
AppInstallMetadata appInstallMetadata = metadataCaptor.getValue();
+ allowedInstallers = allowedInstallersCaptor.getValue();
assertEquals(PACKAGE_NAME, appInstallMetadata.getPackageName());
assertEquals(APP_CERT, appInstallMetadata.getAppCertificate());
assertEquals(INSTALLER_SHA256, appInstallMetadata.getInstallerName());
assertEquals(INSTALLER_CERT, appInstallMetadata.getInstallerCertificate());
assertEquals(VERSION_CODE, appInstallMetadata.getVersionCode());
assertFalse(appInstallMetadata.isPreInstalled());
+ // These are hardcoded in the test apk
+ assertEquals(2, allowedInstallers.size());
+ assertEquals("cert_1", allowedInstallers.get("store_1"));
+ assertEquals("cert_2", allowedInstallers.get("store_2"));
}
@Test
@@ -312,7 +323,7 @@
verify(mMockContext)
.registerReceiver(broadcastReceiverCaptor.capture(), any(), any(), any());
Intent intent = makeVerificationIntent();
- when(mRuleEvaluationEngine.evaluate(any())).thenReturn(IntegrityCheckResult.allow());
+ when(mRuleEvaluationEngine.evaluate(any(), any())).thenReturn(IntegrityCheckResult.allow());
broadcastReceiverCaptor.getValue().onReceive(mMockContext, intent);
runJobInHandler();
@@ -328,7 +339,7 @@
ArgumentCaptor.forClass(BroadcastReceiver.class);
verify(mMockContext)
.registerReceiver(broadcastReceiverCaptor.capture(), any(), any(), any());
- when(mRuleEvaluationEngine.evaluate(any()))
+ when(mRuleEvaluationEngine.evaluate(any(), any()))
.thenReturn(
IntegrityCheckResult.deny(
new Rule(