blob: 6f030649e8c6eb64051e545a587789306edb5226 [file] [log] [blame]
/*
* 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<String> 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<String> 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);
}
}