Merge "Parse manifest rules for integrity component."
This commit is contained in:
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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 {
|
||||
@@ -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 = "";
|
||||
@@ -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);
|
||||
@@ -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: "
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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) {
|
||||
|
||||
@@ -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.
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
Binary file not shown.
@@ -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)
|
||||
@@ -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
|
||||
@@ -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();
|
||||
@@ -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(
|
||||
|
||||
Reference in New Issue
Block a user