/* * Copyright (C) 2015 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.cts.deviceandprofileowner; import static android.app.admin.DevicePolicyManager.DELEGATION_CERT_INSTALL; import static com.google.common.truth.Truth.assertThat; import android.app.admin.DevicePolicyManager; import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.os.Build; import android.os.Process; import android.security.KeyChainException; import android.test.MoreAsserts; import com.android.cts.devicepolicy.TestCertificates; import java.io.ByteArrayInputStream; import java.io.IOException; import java.security.GeneralSecurityException; import java.security.KeyStore; import java.security.KeyStoreException; import java.security.cert.Certificate; import java.security.cert.CertificateFactory; import java.util.Arrays; import java.util.List; import java.util.concurrent.Semaphore; import java.util.concurrent.TimeUnit; /** * Exercise delegated cert installer APIs in {@link DevicePolicyManager} by setting the test app * (CtsCertInstallerApp) as a delegated cert installer and then asking it to invoke various * cert-related APIs. The expected certificate changes are validated both remotely and locally. */ public class DelegatedCertInstallerTest extends BaseDeviceAdminTest { private static final String CERT_INSTALLER_PACKAGE = "com.android.cts.certinstaller"; private static final String NOT_EXIST_CERT_INSTALLER_PACKAGE = "com.android.cts.certinstaller.not_exist"; private static final String ACTION_INSTALL_CERT = "com.android.cts.certinstaller.install_cert"; private static final String ACTION_REMOVE_CERT = "com.android.cts.certinstaller.remove_cert"; private static final String ACTION_VERIFY_CERT = "com.android.cts.certinstaller.verify_cert"; private static final String ACTION_INSTALL_KEYPAIR = "com.android.cts.certinstaller.install_keypair"; private static final String ACTION_CERT_OPERATION_DONE = "com.android.cts.certinstaller.done"; private static final String ACTION_READ_ENROLLMENT_SPECIFIC_ID = "com.android.cts.certinstaller.read_esid"; private static final String EXTRA_CERT_DATA = "extra_cert_data"; private static final String EXTRA_KEY_DATA = "extra_key_data"; private static final String EXTRA_KEY_ALIAS = "extra_key_alias"; private static final String EXTRA_RESULT_VALUE = "extra_result_value"; private static final String EXTRA_RESULT_EXCEPTION = "extra_result_exception"; // package name of receiver has to be specified explicitly as the receiver is registered in // manifest private static final ComponentName CERT_INSTALLER_COMPONENT = new ComponentName( CERT_INSTALLER_PACKAGE, "com.android.cts.certinstaller.CertInstallerReceiver"); private static final List CERT_INSTALL_SCOPES = Arrays.asList(DELEGATION_CERT_INSTALL); private volatile boolean mReceivedResult; private volatile Exception mReceivedException; private Semaphore mAvailableResultSemaphore; private final BroadcastReceiver receiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { if (ACTION_CERT_OPERATION_DONE.equals(intent.getAction())) { synchronized (DelegatedCertInstallerTest.this) { mReceivedResult = intent.getBooleanExtra(EXTRA_RESULT_VALUE, false); mReceivedException = (Exception) intent.getSerializableExtra(EXTRA_RESULT_EXCEPTION); mAvailableResultSemaphore.release(); } } } }; @Override public void setUp() throws Exception { super.setUp(); mAvailableResultSemaphore = new Semaphore(0); mReceivedResult = false; mReceivedException = null; IntentFilter filter = new IntentFilter(); filter.addAction(ACTION_CERT_OPERATION_DONE); mContext.registerReceiver(receiver, filter); } @Override public void tearDown() throws Exception { mContext.unregisterReceiver(receiver); mDevicePolicyManager.uninstallCaCert(ADMIN_RECEIVER_COMPONENT, TestCertificates.TEST_CA.getBytes()); // Installed private key pair will be removed once the lockscreen password is cleared, // which is done in the hostside test. mDevicePolicyManager.setCertInstallerPackage(ADMIN_RECEIVER_COMPONENT, null); super.tearDown(); } public void testCaCertsOperations() throws InterruptedException, GeneralSecurityException, KeyStoreException, IOException { final byte[] cert = TestCertificates.TEST_CA.getBytes(); final Certificate caCert = CertificateFactory.getInstance("X.509") .generateCertificate(new ByteArrayInputStream(cert)); mDevicePolicyManager.setCertInstallerPackage(ADMIN_RECEIVER_COMPONENT, CERT_INSTALLER_PACKAGE); assertEquals(CERT_INSTALLER_PACKAGE, mDevicePolicyManager.getCertInstallerPackage(ADMIN_RECEIVER_COMPONENT)); // Exercise installCaCert() KeyStore keyStore = KeyStore.getInstance("AndroidCAStore"); keyStore.load(null, null); assertNull(keyStore.getCertificateAlias(caCert)); installCaCert(cert); assertResult("installCaCert", true); assertTrue("Certificate is not installed properly", mDevicePolicyManager.hasCaCertInstalled( ADMIN_RECEIVER_COMPONENT, cert)); // Exercise getInstalledCaCerts() verifyCaCert(cert); assertResult("getInstalledCaCerts()", true); // Verify that the CA cert was marked as installed by the Device Owner / Profile Owner. final String alias = keyStore.getCertificateAlias(caCert); assertNotNull(alias); verifyOwnerInstalledStatus(alias, true); // Exercise uninstallCaCert() removeCaCert(cert); assertResult("uninstallCaCert()", true); assertFalse("Certificate is not removed properly", mDevicePolicyManager.hasCaCertInstalled( ADMIN_RECEIVER_COMPONENT, cert)); // Verify that the CA cert is no longer reported as installed by the Device Owner / Profile // Owner. verifyOwnerInstalledStatus(alias, false); // Clear delegated cert installer. // Tests after this are expected to fail. mDevicePolicyManager.setCertInstallerPackage(ADMIN_RECEIVER_COMPONENT, null); installCaCert(cert); assertResult("installCaCert", false); } public void testInstallKeyPair() throws InterruptedException, KeyChainException { final String alias = "delegated-cert-installer-test-key"; // Clear delegated cert installer. mDevicePolicyManager.setCertInstallerPackage(ADMIN_RECEIVER_COMPONENT, null); // The app is not the cert installer , it shouldn't have have privilege to call // installKeyPair(). installKeyPair(TestCertificates.TEST_KEY, TestCertificates.TEST_CERT, alias); assertResult("installKeyPair", false); // Set the app to be cert installer. mDevicePolicyManager.setCertInstallerPackage(ADMIN_RECEIVER_COMPONENT, CERT_INSTALLER_PACKAGE); assertEquals(CERT_INSTALLER_PACKAGE, mDevicePolicyManager.getCertInstallerPackage(ADMIN_RECEIVER_COMPONENT)); // Exercise installKeyPair() installKeyPair(TestCertificates.TEST_KEY, TestCertificates.TEST_CERT, alias); assertResult("installKeyPair", true); } /** * If DPC is targeting N+, @{link IllegalArgumentException } should be thrown if the package * is missing. */ public void testSetNotExistCertInstallerPackage() throws Exception { boolean shouldThrowException = getTargetApiLevel() >= Build.VERSION_CODES.N; try { mDevicePolicyManager.setCertInstallerPackage( ADMIN_RECEIVER_COMPONENT, NOT_EXIST_CERT_INSTALLER_PACKAGE); if (shouldThrowException) { fail("Did not throw IllegalArgumentException"); } } catch (IllegalArgumentException ex) { if (!shouldThrowException) { fail("Should not throw exception"); } MoreAsserts.assertContainsRegex("is not installed on the current user", ex.getMessage()); } if (!shouldThrowException) { assertTrue("Cert install delegate was not set on uninstalled package", NOT_EXIST_CERT_INSTALLER_PACKAGE.equals( mDevicePolicyManager .getCertInstallerPackage(ADMIN_RECEIVER_COMPONENT))); } } public void testSettingDelegatedCertInstallerAPICompatibility_oldSetNewGet() { // Set a delegated cert installer using the deprecated API and verify that the same // package is considered as the delegated cert installer using the new API. mDevicePolicyManager.setCertInstallerPackage(ADMIN_RECEIVER_COMPONENT, CERT_INSTALLER_PACKAGE); assertThat(mDevicePolicyManager.getCertInstallerPackage(ADMIN_RECEIVER_COMPONENT)) .isEqualTo(CERT_INSTALLER_PACKAGE); assertThat(mDevicePolicyManager.getDelegatePackages(ADMIN_RECEIVER_COMPONENT, DELEGATION_CERT_INSTALL)).containsExactly(CERT_INSTALLER_PACKAGE); // Remove a delegate using the old API, make sure no delegates are found using // the new API. mDevicePolicyManager.setCertInstallerPackage(ADMIN_RECEIVER_COMPONENT, null); assertThat(mDevicePolicyManager.getCertInstallerPackage(ADMIN_RECEIVER_COMPONENT)).isNull(); assertThat(mDevicePolicyManager.getDelegatePackages(ADMIN_RECEIVER_COMPONENT, DELEGATION_CERT_INSTALL)).isEmpty(); } public void testSettingDelegatedCertInstallerAPICompatibility_newSetOldGet() { // Set a delegate using the new API, verify that the deprecated API returns the same // delegate. setDelegatedScopes(CERT_INSTALLER_PACKAGE, CERT_INSTALL_SCOPES); assertThat(mDevicePolicyManager.getDelegatePackages(ADMIN_RECEIVER_COMPONENT, DELEGATION_CERT_INSTALL)).containsExactly(CERT_INSTALLER_PACKAGE); assertThat(mDevicePolicyManager.getCertInstallerPackage(ADMIN_RECEIVER_COMPONENT)) .isEqualTo(CERT_INSTALLER_PACKAGE); // Remove the delegate using the new API, verify that the deprecated API returns null // as the current delegated cert installer. setDelegatedScopes(CERT_INSTALLER_PACKAGE, Arrays.asList()); assertThat(mDevicePolicyManager.getDelegatePackages(ADMIN_RECEIVER_COMPONENT, DELEGATION_CERT_INSTALL)).isEmpty(); assertThat(mDevicePolicyManager.getCertInstallerPackage(ADMIN_RECEIVER_COMPONENT)).isNull(); } public void testCanReadEnrollmentSpecificId() throws InterruptedException { // Set the organization ID only if not already set, to avoid potential conflict // with other tests. if (mDevicePolicyManager.getEnrollmentSpecificId().isEmpty()) { mDevicePolicyManager.setOrganizationId("SOME_ID"); } setDelegatedScopes(CERT_INSTALLER_PACKAGE, CERT_INSTALL_SCOPES); readEnrollmentId(); assertResult("testCanReadEnrollmentSpecificId", true); } private void installCaCert(byte[] cert) { Intent intent = new Intent(); intent.setAction(ACTION_INSTALL_CERT); intent.setComponent(CERT_INSTALLER_COMPONENT); intent.putExtra(EXTRA_CERT_DATA, cert); intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND); mContext.sendBroadcast(intent); } private void removeCaCert(byte[] cert) { Intent intent = new Intent(); intent.setAction(ACTION_REMOVE_CERT); intent.setComponent(CERT_INSTALLER_COMPONENT); intent.putExtra(EXTRA_CERT_DATA, cert); intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND); mContext.sendBroadcast(intent); } private void verifyCaCert(byte[] cert) { Intent intent = new Intent(); intent.setAction(ACTION_VERIFY_CERT); intent.setComponent(CERT_INSTALLER_COMPONENT); intent.putExtra(EXTRA_CERT_DATA, cert); intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND); mContext.sendBroadcast(intent); } private void verifyOwnerInstalledStatus(String alias, boolean expectOwnerInstalled) { final List ownerInstalledCerts = mDevicePolicyManager.getOwnerInstalledCaCerts(Process.myUserHandle()); assertNotNull(ownerInstalledCerts); assertEquals(expectOwnerInstalled, ownerInstalledCerts.contains(alias)); } private void assertResult(String testName, Boolean expectSuccess) throws InterruptedException { assertTrue("Cert installer did not respond in time.", mAvailableResultSemaphore.tryAcquire(180, TimeUnit.SECONDS)); synchronized (this) { if (expectSuccess) { assertTrue(testName + " failed unexpectedly.", mReceivedResult); assertNull(testName + " raised exception", mReceivedException); } else { assertFalse(testName + " succeeded unexpectedly.", mReceivedResult); assertTrue(testName + " did not raise SecurityException", mReceivedException != null && mReceivedException instanceof SecurityException); } } } private void installKeyPair(String key, String cert, String alias) { Intent intent = new Intent(); intent.setAction(ACTION_INSTALL_KEYPAIR); intent.setComponent(CERT_INSTALLER_COMPONENT); intent.putExtra(EXTRA_CERT_DATA, cert); intent.putExtra(EXTRA_KEY_DATA, key); intent.putExtra(EXTRA_KEY_ALIAS, alias); intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND); mContext.sendBroadcast(intent); } private void readEnrollmentId() { Intent intent = new Intent(); intent.setAction(ACTION_READ_ENROLLMENT_SPECIFIC_ID); intent.setComponent(CERT_INSTALLER_COMPONENT); intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND); mContext.sendBroadcast(intent); } }