Loading services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java +65 −11 Original line number Diff line number Diff line Loading @@ -25,6 +25,7 @@ import static android.content.integrity.AppIntegrityManager.STATUS_FAILURE; 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; Loading @@ -41,6 +42,7 @@ import android.content.pm.ParceledListSlice; 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; Loading @@ -63,6 +65,8 @@ import java.security.cert.CertificateEncodingException; 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 { Loading @@ -72,6 +76,9 @@ public class AppIntegrityManagerServiceImpl extends IAppIntegrityManager.Stub { 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 = ""; Loading Loading @@ -191,11 +198,21 @@ public class AppIntegrityManagerServiceImpl extends IAppIntegrityManager.Stub { 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); Loading @@ -208,7 +225,9 @@ public class AppIntegrityManagerServiceImpl extends IAppIntegrityManager.Stub { 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: " Loading @@ -224,14 +243,12 @@ public class AppIntegrityManagerServiceImpl extends IAppIntegrityManager.Stub { // 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); } Loading Loading @@ -320,8 +337,7 @@ public class AppIntegrityManagerServiceImpl extends IAppIntegrityManager.Stub { } } private String getAppCertificateFingerprint(Uri dataUri) { PackageInfo packageInfo = getPackageArchiveInfo(dataUri); private String getCertificateFingerprint(@NonNull PackageInfo packageInfo) { return getFingerprint(getSignature(packageInfo)); } Loading @@ -333,14 +349,50 @@ public class AppIntegrityManagerServiceImpl extends IAppIntegrityManager.Stub { 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); } Loading Loading @@ -402,7 +454,9 @@ public class AppIntegrityManagerServiceImpl extends IAppIntegrityManager.Stub { packageInfo = mContext.getPackageManager() .getPackageArchiveInfo( installationPath.getPath(), PackageManager.GET_SIGNATURES); installationPath.getPath(), PackageManager.GET_SIGNATURES | PackageManager.GET_META_DATA); } return packageInfo; } catch (Exception e) { Loading services/core/java/com/android/server/integrity/engine/RuleEvaluationEngine.java +3 −1 Original line number Diff line number Diff line Loading @@ -25,6 +25,7 @@ import com.android.server.integrity.model.IntegrityCheckResult; import java.util.ArrayList; import java.util.List; import java.util.Map; /** * The engine used to evaluate rules against app installs. Loading Loading @@ -60,7 +61,8 @@ public class RuleEvaluationEngine { * 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); } Loading services/tests/servicestests/assets/AppIntegrityManagerServiceImplTest/test.apk +1.26 MiB (1.3 MiB) File changed.No diff preview for this file type. View original file View changed file services/tests/servicestests/src/com/android/server/integrity/AppIntegrityManagerServiceImplTest.java +15 −4 Original line number Diff line number Diff line Loading @@ -79,7 +79,9 @@ import java.io.InputStream; 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) Loading Loading @@ -288,21 +290,30 @@ public class AppIntegrityManagerServiceImplTest { 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 Loading @@ -312,7 +323,7 @@ public class AppIntegrityManagerServiceImplTest { 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(); Loading @@ -328,7 +339,7 @@ public class AppIntegrityManagerServiceImplTest { 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( Loading Loading
services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java +65 −11 Original line number Diff line number Diff line Loading @@ -25,6 +25,7 @@ import static android.content.integrity.AppIntegrityManager.STATUS_FAILURE; 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; Loading @@ -41,6 +42,7 @@ import android.content.pm.ParceledListSlice; 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; Loading @@ -63,6 +65,8 @@ import java.security.cert.CertificateEncodingException; 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 { Loading @@ -72,6 +76,9 @@ public class AppIntegrityManagerServiceImpl extends IAppIntegrityManager.Stub { 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 = ""; Loading Loading @@ -191,11 +198,21 @@ public class AppIntegrityManagerServiceImpl extends IAppIntegrityManager.Stub { 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); Loading @@ -208,7 +225,9 @@ public class AppIntegrityManagerServiceImpl extends IAppIntegrityManager.Stub { 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: " Loading @@ -224,14 +243,12 @@ public class AppIntegrityManagerServiceImpl extends IAppIntegrityManager.Stub { // 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); } Loading Loading @@ -320,8 +337,7 @@ public class AppIntegrityManagerServiceImpl extends IAppIntegrityManager.Stub { } } private String getAppCertificateFingerprint(Uri dataUri) { PackageInfo packageInfo = getPackageArchiveInfo(dataUri); private String getCertificateFingerprint(@NonNull PackageInfo packageInfo) { return getFingerprint(getSignature(packageInfo)); } Loading @@ -333,14 +349,50 @@ public class AppIntegrityManagerServiceImpl extends IAppIntegrityManager.Stub { 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); } Loading Loading @@ -402,7 +454,9 @@ public class AppIntegrityManagerServiceImpl extends IAppIntegrityManager.Stub { packageInfo = mContext.getPackageManager() .getPackageArchiveInfo( installationPath.getPath(), PackageManager.GET_SIGNATURES); installationPath.getPath(), PackageManager.GET_SIGNATURES | PackageManager.GET_META_DATA); } return packageInfo; } catch (Exception e) { Loading
services/core/java/com/android/server/integrity/engine/RuleEvaluationEngine.java +3 −1 Original line number Diff line number Diff line Loading @@ -25,6 +25,7 @@ import com.android.server.integrity.model.IntegrityCheckResult; import java.util.ArrayList; import java.util.List; import java.util.Map; /** * The engine used to evaluate rules against app installs. Loading Loading @@ -60,7 +61,8 @@ public class RuleEvaluationEngine { * 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); } Loading
services/tests/servicestests/assets/AppIntegrityManagerServiceImplTest/test.apk +1.26 MiB (1.3 MiB) File changed.No diff preview for this file type. View original file View changed file
services/tests/servicestests/src/com/android/server/integrity/AppIntegrityManagerServiceImplTest.java +15 −4 Original line number Diff line number Diff line Loading @@ -79,7 +79,9 @@ import java.io.InputStream; 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) Loading Loading @@ -288,21 +290,30 @@ public class AppIntegrityManagerServiceImplTest { 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 Loading @@ -312,7 +323,7 @@ public class AppIntegrityManagerServiceImplTest { 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(); Loading @@ -328,7 +339,7 @@ public class AppIntegrityManagerServiceImplTest { 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( Loading