1 /* 2 * Copyright (C) 2015 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.cts.deviceandprofileowner; 18 19 import static android.app.admin.DevicePolicyManager.DELEGATION_CERT_INSTALL; 20 21 import static com.google.common.truth.Truth.assertThat; 22 23 import android.app.admin.DevicePolicyManager; 24 import android.content.BroadcastReceiver; 25 import android.content.ComponentName; 26 import android.content.Context; 27 import android.content.Intent; 28 import android.content.IntentFilter; 29 import android.os.Build; 30 import android.os.Process; 31 import android.security.KeyChainException; 32 import android.test.MoreAsserts; 33 34 import com.android.cts.devicepolicy.TestCertificates; 35 36 import java.io.ByteArrayInputStream; 37 import java.io.IOException; 38 import java.security.GeneralSecurityException; 39 import java.security.KeyStore; 40 import java.security.KeyStoreException; 41 import java.security.cert.Certificate; 42 import java.security.cert.CertificateFactory; 43 import java.util.Arrays; 44 import java.util.List; 45 import java.util.concurrent.Semaphore; 46 import java.util.concurrent.TimeUnit; 47 48 /** 49 * Exercise delegated cert installer APIs in {@link DevicePolicyManager} by setting the test app 50 * (CtsCertInstallerApp) as a delegated cert installer and then asking it to invoke various 51 * cert-related APIs. The expected certificate changes are validated both remotely and locally. 52 */ 53 public class DelegatedCertInstallerTest extends BaseDeviceAdminTest { 54 55 private static final String CERT_INSTALLER_PACKAGE = "com.android.cts.certinstaller"; 56 private static final String NOT_EXIST_CERT_INSTALLER_PACKAGE 57 = "com.android.cts.certinstaller.not_exist"; 58 59 private static final String ACTION_INSTALL_CERT = "com.android.cts.certinstaller.install_cert"; 60 private static final String ACTION_REMOVE_CERT = "com.android.cts.certinstaller.remove_cert"; 61 private static final String ACTION_VERIFY_CERT = "com.android.cts.certinstaller.verify_cert"; 62 private static final String ACTION_INSTALL_KEYPAIR = 63 "com.android.cts.certinstaller.install_keypair"; 64 private static final String ACTION_CERT_OPERATION_DONE = "com.android.cts.certinstaller.done"; 65 private static final String ACTION_READ_ENROLLMENT_SPECIFIC_ID = 66 "com.android.cts.certinstaller.read_esid"; 67 68 private static final String EXTRA_CERT_DATA = "extra_cert_data"; 69 private static final String EXTRA_KEY_DATA = "extra_key_data"; 70 private static final String EXTRA_KEY_ALIAS = "extra_key_alias"; 71 private static final String EXTRA_RESULT_VALUE = "extra_result_value"; 72 private static final String EXTRA_RESULT_EXCEPTION = "extra_result_exception"; 73 // package name of receiver has to be specified explicitly as the receiver is registered in 74 // manifest 75 private static final ComponentName CERT_INSTALLER_COMPONENT = new ComponentName( 76 CERT_INSTALLER_PACKAGE, "com.android.cts.certinstaller.CertInstallerReceiver"); 77 78 private static final List<String> CERT_INSTALL_SCOPES = Arrays.asList(DELEGATION_CERT_INSTALL); 79 80 private DevicePolicyManager mDpm; 81 private volatile boolean mReceivedResult; 82 private volatile Exception mReceivedException; 83 private Semaphore mAvailableResultSemaphore; 84 85 private final BroadcastReceiver receiver = new BroadcastReceiver() { 86 @Override 87 public void onReceive(Context context, Intent intent) { 88 if (ACTION_CERT_OPERATION_DONE.equals(intent.getAction())) { 89 synchronized (DelegatedCertInstallerTest.this) { 90 mReceivedResult = intent.getBooleanExtra(EXTRA_RESULT_VALUE, false); 91 mReceivedException = 92 (Exception) intent.getSerializableExtra(EXTRA_RESULT_EXCEPTION); 93 mAvailableResultSemaphore.release(); 94 } 95 } 96 } 97 }; 98 99 @Override setUp()100 public void setUp() throws Exception { 101 super.setUp(); 102 mDpm = (DevicePolicyManager) mContext.getSystemService(Context.DEVICE_POLICY_SERVICE); 103 mAvailableResultSemaphore = new Semaphore(0); 104 mReceivedResult = false; 105 mReceivedException = null; 106 IntentFilter filter = new IntentFilter(); 107 filter.addAction(ACTION_CERT_OPERATION_DONE); 108 mContext.registerReceiver(receiver, filter); 109 } 110 111 @Override tearDown()112 public void tearDown() throws Exception { 113 mContext.unregisterReceiver(receiver); 114 mDpm.uninstallCaCert(ADMIN_RECEIVER_COMPONENT, TestCertificates.TEST_CA.getBytes()); 115 // Installed private key pair will be removed once the lockscreen password is cleared, 116 // which is done in the hostside test. 117 mDpm.setCertInstallerPackage(ADMIN_RECEIVER_COMPONENT, null); 118 super.tearDown(); 119 } 120 testCaCertsOperations()121 public void testCaCertsOperations() throws InterruptedException, GeneralSecurityException, 122 KeyStoreException, IOException { 123 final byte[] cert = TestCertificates.TEST_CA.getBytes(); 124 final Certificate caCert = CertificateFactory.getInstance("X.509") 125 .generateCertificate(new ByteArrayInputStream(cert)); 126 127 128 mDpm.setCertInstallerPackage(ADMIN_RECEIVER_COMPONENT, CERT_INSTALLER_PACKAGE); 129 assertEquals(CERT_INSTALLER_PACKAGE, 130 mDpm.getCertInstallerPackage(ADMIN_RECEIVER_COMPONENT)); 131 132 // Exercise installCaCert() 133 KeyStore keyStore = KeyStore.getInstance("AndroidCAStore"); 134 keyStore.load(null, null); 135 assertNull(keyStore.getCertificateAlias(caCert)); 136 installCaCert(cert); 137 assertResult("installCaCert", true); 138 assertTrue("Certificate is not installed properly", mDpm.hasCaCertInstalled( 139 ADMIN_RECEIVER_COMPONENT, cert)); 140 141 // Exercise getInstalledCaCerts() 142 verifyCaCert(cert); 143 assertResult("getInstalledCaCerts()", true); 144 145 // Verify that the CA cert was marked as installed by the Device Owner / Profile Owner. 146 final String alias = keyStore.getCertificateAlias(caCert); 147 assertNotNull(alias); 148 verifyOwnerInstalledStatus(alias, true); 149 150 // Exercise uninstallCaCert() 151 removeCaCert(cert); 152 assertResult("uninstallCaCert()", true); 153 assertFalse("Certificate is not removed properly", mDpm.hasCaCertInstalled( 154 ADMIN_RECEIVER_COMPONENT, cert)); 155 156 // Verify that the CA cert is no longer reported as installed by the Device Owner / Profile 157 // Owner. 158 verifyOwnerInstalledStatus(alias, false); 159 160 // Clear delegated cert installer. 161 // Tests after this are expected to fail. 162 mDpm.setCertInstallerPackage(ADMIN_RECEIVER_COMPONENT, null); 163 164 installCaCert(cert); 165 assertResult("installCaCert", false); 166 } 167 testInstallKeyPair()168 public void testInstallKeyPair() throws InterruptedException, KeyChainException { 169 final String alias = "delegated-cert-installer-test-key"; 170 171 // Clear delegated cert installer. 172 mDpm.setCertInstallerPackage(ADMIN_RECEIVER_COMPONENT, null); 173 // The app is not the cert installer , it shouldn't have have privilege to call 174 // installKeyPair(). 175 installKeyPair(TestCertificates.TEST_KEY, TestCertificates.TEST_CERT, alias); 176 assertResult("installKeyPair", false); 177 178 // Set the app to be cert installer. 179 mDpm.setCertInstallerPackage(ADMIN_RECEIVER_COMPONENT, CERT_INSTALLER_PACKAGE); 180 assertEquals(CERT_INSTALLER_PACKAGE, 181 mDpm.getCertInstallerPackage(ADMIN_RECEIVER_COMPONENT)); 182 183 // Exercise installKeyPair() 184 installKeyPair(TestCertificates.TEST_KEY, TestCertificates.TEST_CERT, alias); 185 assertResult("installKeyPair", true); 186 } 187 188 /** 189 * If DPC is targeting N+, @{link IllegalArgumentException } should be thrown if the package 190 * is missing. 191 */ testSetNotExistCertInstallerPackage()192 public void testSetNotExistCertInstallerPackage() throws Exception { 193 boolean shouldThrowException = getTargetApiLevel() >= Build.VERSION_CODES.N; 194 try { 195 mDpm.setCertInstallerPackage( 196 ADMIN_RECEIVER_COMPONENT, NOT_EXIST_CERT_INSTALLER_PACKAGE); 197 if (shouldThrowException) { 198 fail("Did not throw IllegalArgumentException"); 199 } 200 } catch (IllegalArgumentException ex) { 201 if (!shouldThrowException) { 202 fail("Should not throw exception"); 203 } 204 MoreAsserts.assertContainsRegex("is not installed on the current user", 205 ex.getMessage()); 206 } 207 if (!shouldThrowException) { 208 assertTrue("Cert install delegate was not set on uninstalled package", 209 NOT_EXIST_CERT_INSTALLER_PACKAGE.equals( 210 mDpm.getCertInstallerPackage(ADMIN_RECEIVER_COMPONENT))); 211 } 212 } 213 testSettingDelegatedCertInstallerAPICompatibility_oldSetNewGet()214 public void testSettingDelegatedCertInstallerAPICompatibility_oldSetNewGet() { 215 // Set a delegated cert installer using the deprecated API and verify that the same 216 // package is considered as the delegated cert installer using the new API. 217 mDpm.setCertInstallerPackage(ADMIN_RECEIVER_COMPONENT, CERT_INSTALLER_PACKAGE); 218 assertThat(mDpm.getCertInstallerPackage(ADMIN_RECEIVER_COMPONENT)).isEqualTo( 219 CERT_INSTALLER_PACKAGE); 220 assertThat(mDpm.getDelegatePackages(ADMIN_RECEIVER_COMPONENT, 221 DELEGATION_CERT_INSTALL)).containsExactly(CERT_INSTALLER_PACKAGE); 222 223 // Remove a delegate using the old API, make sure no delegates are found using 224 // the new API. 225 mDpm.setCertInstallerPackage(ADMIN_RECEIVER_COMPONENT, null); 226 assertThat(mDpm.getCertInstallerPackage(ADMIN_RECEIVER_COMPONENT)).isNull(); 227 assertThat(mDpm.getDelegatePackages(ADMIN_RECEIVER_COMPONENT, 228 DELEGATION_CERT_INSTALL)).isEmpty(); 229 } 230 testSettingDelegatedCertInstallerAPICompatibility_newSetOldGet()231 public void testSettingDelegatedCertInstallerAPICompatibility_newSetOldGet() { 232 // Set a delegate using the new API, verify that the deprecated API returns the same 233 // delegate. 234 mDpm.setDelegatedScopes(ADMIN_RECEIVER_COMPONENT, CERT_INSTALLER_PACKAGE, 235 CERT_INSTALL_SCOPES); 236 assertThat(mDpm.getDelegatePackages(ADMIN_RECEIVER_COMPONENT, 237 DELEGATION_CERT_INSTALL)).containsExactly(CERT_INSTALLER_PACKAGE); 238 assertThat(mDpm.getCertInstallerPackage(ADMIN_RECEIVER_COMPONENT)).isEqualTo( 239 CERT_INSTALLER_PACKAGE); 240 241 // Remove the delegate using the new API, verify that the deprecated API returns null 242 // as the current delegated cert installer. 243 mDpm.setDelegatedScopes(ADMIN_RECEIVER_COMPONENT, CERT_INSTALLER_PACKAGE, Arrays.asList()); 244 assertThat(mDpm.getDelegatePackages(ADMIN_RECEIVER_COMPONENT, 245 DELEGATION_CERT_INSTALL)).isEmpty(); 246 assertThat(mDpm.getCertInstallerPackage(ADMIN_RECEIVER_COMPONENT)).isNull(); 247 } 248 testCanReadEnrollmentSpecificId()249 public void testCanReadEnrollmentSpecificId() throws InterruptedException { 250 // Set the organization ID only if not already set, to avoid potential conflict 251 // with other tests. 252 if (mDpm.getEnrollmentSpecificId().isEmpty()) { 253 mDpm.setOrganizationId("SOME_ID"); 254 } 255 mDpm.setDelegatedScopes(ADMIN_RECEIVER_COMPONENT, CERT_INSTALLER_PACKAGE, 256 CERT_INSTALL_SCOPES); 257 258 readEnrollmentId(); 259 assertResult("testCanReadEnrollmentSpecificId", true); 260 } 261 installCaCert(byte[] cert)262 private void installCaCert(byte[] cert) { 263 Intent intent = new Intent(); 264 intent.setAction(ACTION_INSTALL_CERT); 265 intent.setComponent(CERT_INSTALLER_COMPONENT); 266 intent.putExtra(EXTRA_CERT_DATA, cert); 267 intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND); 268 mContext.sendBroadcast(intent); 269 } 270 removeCaCert(byte[] cert)271 private void removeCaCert(byte[] cert) { 272 Intent intent = new Intent(); 273 intent.setAction(ACTION_REMOVE_CERT); 274 intent.setComponent(CERT_INSTALLER_COMPONENT); 275 intent.putExtra(EXTRA_CERT_DATA, cert); 276 intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND); 277 mContext.sendBroadcast(intent); 278 } 279 verifyCaCert(byte[] cert)280 private void verifyCaCert(byte[] cert) { 281 Intent intent = new Intent(); 282 intent.setAction(ACTION_VERIFY_CERT); 283 intent.setComponent(CERT_INSTALLER_COMPONENT); 284 intent.putExtra(EXTRA_CERT_DATA, cert); 285 intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND); 286 mContext.sendBroadcast(intent); 287 } 288 verifyOwnerInstalledStatus(String alias, boolean expectOwnerInstalled)289 private void verifyOwnerInstalledStatus(String alias, boolean expectOwnerInstalled) { 290 final List<String> ownerInstalledCerts = 291 mDpm.getOwnerInstalledCaCerts(Process.myUserHandle()); 292 assertNotNull(ownerInstalledCerts); 293 assertEquals(expectOwnerInstalled, ownerInstalledCerts.contains(alias)); 294 } 295 assertResult(String testName, Boolean expectSuccess)296 private void assertResult(String testName, Boolean expectSuccess) throws InterruptedException { 297 assertTrue("Cert installer did not respond in time.", 298 mAvailableResultSemaphore.tryAcquire(180, TimeUnit.SECONDS)); 299 synchronized (this) { 300 if (expectSuccess) { 301 assertTrue(testName + " failed unexpectedly.", mReceivedResult); 302 assertNull(testName + " raised exception", mReceivedException); 303 } else { 304 assertFalse(testName + " succeeded unexpectedly.", mReceivedResult); 305 assertTrue(testName + " did not raise SecurityException", 306 mReceivedException != null && 307 mReceivedException instanceof SecurityException); 308 } 309 } 310 } 311 installKeyPair(String key, String cert, String alias)312 private void installKeyPair(String key, String cert, String alias) { 313 Intent intent = new Intent(); 314 intent.setAction(ACTION_INSTALL_KEYPAIR); 315 intent.setComponent(CERT_INSTALLER_COMPONENT); 316 intent.putExtra(EXTRA_CERT_DATA, cert); 317 intent.putExtra(EXTRA_KEY_DATA, key); 318 intent.putExtra(EXTRA_KEY_ALIAS, alias); 319 intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND); 320 mContext.sendBroadcast(intent); 321 } 322 readEnrollmentId()323 private void readEnrollmentId() { 324 Intent intent = new Intent(); 325 intent.setAction(ACTION_READ_ENROLLMENT_SPECIFIC_ID); 326 intent.setComponent(CERT_INSTALLER_COMPONENT); 327 intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND); 328 mContext.sendBroadcast(intent); 329 } 330 } 331