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 volatile boolean mReceivedResult;
81     private volatile Exception mReceivedException;
82     private Semaphore mAvailableResultSemaphore;
83 
84     private final BroadcastReceiver receiver = new BroadcastReceiver() {
85         @Override
86         public void onReceive(Context context, Intent intent) {
87             if (ACTION_CERT_OPERATION_DONE.equals(intent.getAction())) {
88                 synchronized (DelegatedCertInstallerTest.this) {
89                     mReceivedResult = intent.getBooleanExtra(EXTRA_RESULT_VALUE, false);
90                     mReceivedException =
91                             (Exception) intent.getSerializableExtra(EXTRA_RESULT_EXCEPTION);
92                     mAvailableResultSemaphore.release();
93                 }
94             }
95         }
96     };
97 
98     @Override
setUp()99     public void setUp() throws Exception {
100         super.setUp();
101 
102         mAvailableResultSemaphore = new Semaphore(0);
103         mReceivedResult = false;
104         mReceivedException = null;
105         IntentFilter filter = new IntentFilter();
106         filter.addAction(ACTION_CERT_OPERATION_DONE);
107         mContext.registerReceiver(receiver, filter);
108     }
109 
110     @Override
tearDown()111     public void tearDown() throws Exception {
112         mContext.unregisterReceiver(receiver);
113         mDevicePolicyManager.uninstallCaCert(ADMIN_RECEIVER_COMPONENT,
114                 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         mDevicePolicyManager.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         mDevicePolicyManager.setCertInstallerPackage(ADMIN_RECEIVER_COMPONENT,
129                 CERT_INSTALLER_PACKAGE);
130         assertEquals(CERT_INSTALLER_PACKAGE,
131                 mDevicePolicyManager.getCertInstallerPackage(ADMIN_RECEIVER_COMPONENT));
132 
133         // Exercise installCaCert()
134         KeyStore keyStore = KeyStore.getInstance("AndroidCAStore");
135         keyStore.load(null, null);
136         assertNull(keyStore.getCertificateAlias(caCert));
137         installCaCert(cert);
138         assertResult("installCaCert", true);
139         assertTrue("Certificate is not installed properly", mDevicePolicyManager.hasCaCertInstalled(
140                 ADMIN_RECEIVER_COMPONENT, cert));
141 
142         // Exercise getInstalledCaCerts()
143         verifyCaCert(cert);
144         assertResult("getInstalledCaCerts()", true);
145 
146         // Verify that the CA cert was marked as installed by the Device Owner / Profile Owner.
147         final String alias = keyStore.getCertificateAlias(caCert);
148         assertNotNull(alias);
149         verifyOwnerInstalledStatus(alias, true);
150 
151         // Exercise uninstallCaCert()
152         removeCaCert(cert);
153         assertResult("uninstallCaCert()", true);
154         assertFalse("Certificate is not removed properly", mDevicePolicyManager.hasCaCertInstalled(
155                 ADMIN_RECEIVER_COMPONENT, cert));
156 
157         // Verify that the CA cert is no longer reported as installed by the Device Owner / Profile
158         // Owner.
159         verifyOwnerInstalledStatus(alias, false);
160 
161         // Clear delegated cert installer.
162         // Tests after this are expected to fail.
163         mDevicePolicyManager.setCertInstallerPackage(ADMIN_RECEIVER_COMPONENT, null);
164 
165         installCaCert(cert);
166         assertResult("installCaCert", false);
167     }
168 
testInstallKeyPair()169     public void testInstallKeyPair() throws InterruptedException, KeyChainException {
170         final String alias = "delegated-cert-installer-test-key";
171 
172         // Clear delegated cert installer.
173         mDevicePolicyManager.setCertInstallerPackage(ADMIN_RECEIVER_COMPONENT, null);
174         // The app is not the cert installer , it shouldn't have have privilege to call
175         // installKeyPair().
176         installKeyPair(TestCertificates.TEST_KEY, TestCertificates.TEST_CERT, alias);
177         assertResult("installKeyPair", false);
178 
179         // Set the app to be cert installer.
180         mDevicePolicyManager.setCertInstallerPackage(ADMIN_RECEIVER_COMPONENT,
181                 CERT_INSTALLER_PACKAGE);
182         assertEquals(CERT_INSTALLER_PACKAGE,
183                 mDevicePolicyManager.getCertInstallerPackage(ADMIN_RECEIVER_COMPONENT));
184 
185         // Exercise installKeyPair()
186         installKeyPair(TestCertificates.TEST_KEY, TestCertificates.TEST_CERT, alias);
187         assertResult("installKeyPair", true);
188     }
189 
190     /**
191      * If DPC is targeting N+, @{link IllegalArgumentException } should be thrown if the package
192      * is missing.
193      */
testSetNotExistCertInstallerPackage()194     public void testSetNotExistCertInstallerPackage() throws Exception {
195         boolean shouldThrowException = getTargetApiLevel() >= Build.VERSION_CODES.N;
196         try {
197             mDevicePolicyManager.setCertInstallerPackage(
198                     ADMIN_RECEIVER_COMPONENT, NOT_EXIST_CERT_INSTALLER_PACKAGE);
199             if (shouldThrowException) {
200                 fail("Did not throw IllegalArgumentException");
201             }
202         } catch (IllegalArgumentException ex) {
203             if (!shouldThrowException) {
204                 fail("Should not throw exception");
205             }
206             MoreAsserts.assertContainsRegex("is not installed on the current user",
207                         ex.getMessage());
208         }
209         if (!shouldThrowException) {
210             assertTrue("Cert install delegate was not set on uninstalled package",
211                     NOT_EXIST_CERT_INSTALLER_PACKAGE.equals(
212                             mDevicePolicyManager
213                                     .getCertInstallerPackage(ADMIN_RECEIVER_COMPONENT)));
214         }
215     }
216 
testSettingDelegatedCertInstallerAPICompatibility_oldSetNewGet()217     public void testSettingDelegatedCertInstallerAPICompatibility_oldSetNewGet() {
218         // Set a delegated cert installer using the deprecated API and verify that the same
219         // package is considered as the delegated cert installer using the new API.
220         mDevicePolicyManager.setCertInstallerPackage(ADMIN_RECEIVER_COMPONENT,
221                 CERT_INSTALLER_PACKAGE);
222         assertThat(mDevicePolicyManager.getCertInstallerPackage(ADMIN_RECEIVER_COMPONENT))
223                 .isEqualTo(CERT_INSTALLER_PACKAGE);
224         assertThat(mDevicePolicyManager.getDelegatePackages(ADMIN_RECEIVER_COMPONENT,
225                 DELEGATION_CERT_INSTALL)).containsExactly(CERT_INSTALLER_PACKAGE);
226 
227         // Remove a delegate using the old API, make sure no delegates are found using
228         // the new API.
229         mDevicePolicyManager.setCertInstallerPackage(ADMIN_RECEIVER_COMPONENT, null);
230         assertThat(mDevicePolicyManager.getCertInstallerPackage(ADMIN_RECEIVER_COMPONENT)).isNull();
231         assertThat(mDevicePolicyManager.getDelegatePackages(ADMIN_RECEIVER_COMPONENT,
232                 DELEGATION_CERT_INSTALL)).isEmpty();
233     }
234 
testSettingDelegatedCertInstallerAPICompatibility_newSetOldGet()235     public void testSettingDelegatedCertInstallerAPICompatibility_newSetOldGet() {
236         // Set a delegate using the new API, verify that the deprecated API returns the same
237         // delegate.
238         setDelegatedScopes(CERT_INSTALLER_PACKAGE, CERT_INSTALL_SCOPES);
239         assertThat(mDevicePolicyManager.getDelegatePackages(ADMIN_RECEIVER_COMPONENT,
240                 DELEGATION_CERT_INSTALL)).containsExactly(CERT_INSTALLER_PACKAGE);
241         assertThat(mDevicePolicyManager.getCertInstallerPackage(ADMIN_RECEIVER_COMPONENT))
242                 .isEqualTo(CERT_INSTALLER_PACKAGE);
243 
244         // Remove the delegate using the new API, verify that the deprecated API returns null
245         // as the current delegated cert installer.
246         setDelegatedScopes(CERT_INSTALLER_PACKAGE, Arrays.asList());
247         assertThat(mDevicePolicyManager.getDelegatePackages(ADMIN_RECEIVER_COMPONENT,
248                 DELEGATION_CERT_INSTALL)).isEmpty();
249         assertThat(mDevicePolicyManager.getCertInstallerPackage(ADMIN_RECEIVER_COMPONENT)).isNull();
250     }
251 
testCanReadEnrollmentSpecificId()252     public void testCanReadEnrollmentSpecificId() throws InterruptedException {
253         // Set the organization ID only if not already set, to avoid potential conflict
254         // with other tests.
255         if (mDevicePolicyManager.getEnrollmentSpecificId().isEmpty()) {
256             mDevicePolicyManager.setOrganizationId("SOME_ID");
257         }
258         setDelegatedScopes(CERT_INSTALLER_PACKAGE, CERT_INSTALL_SCOPES);
259 
260         readEnrollmentId();
261         assertResult("testCanReadEnrollmentSpecificId", true);
262     }
263 
installCaCert(byte[] cert)264     private void installCaCert(byte[] cert) {
265         Intent intent = new Intent();
266         intent.setAction(ACTION_INSTALL_CERT);
267         intent.setComponent(CERT_INSTALLER_COMPONENT);
268         intent.putExtra(EXTRA_CERT_DATA, cert);
269         intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
270         mContext.sendBroadcast(intent);
271     }
272 
removeCaCert(byte[] cert)273     private void removeCaCert(byte[] cert) {
274         Intent intent = new Intent();
275         intent.setAction(ACTION_REMOVE_CERT);
276         intent.setComponent(CERT_INSTALLER_COMPONENT);
277         intent.putExtra(EXTRA_CERT_DATA, cert);
278         intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
279         mContext.sendBroadcast(intent);
280     }
281 
verifyCaCert(byte[] cert)282     private void verifyCaCert(byte[] cert) {
283         Intent intent = new Intent();
284         intent.setAction(ACTION_VERIFY_CERT);
285         intent.setComponent(CERT_INSTALLER_COMPONENT);
286         intent.putExtra(EXTRA_CERT_DATA, cert);
287         intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
288         mContext.sendBroadcast(intent);
289     }
290 
verifyOwnerInstalledStatus(String alias, boolean expectOwnerInstalled)291     private void verifyOwnerInstalledStatus(String alias, boolean expectOwnerInstalled) {
292         final List<String> ownerInstalledCerts =
293                 mDevicePolicyManager.getOwnerInstalledCaCerts(Process.myUserHandle());
294         assertNotNull(ownerInstalledCerts);
295         assertEquals(expectOwnerInstalled, ownerInstalledCerts.contains(alias));
296     }
297 
assertResult(String testName, Boolean expectSuccess)298     private void assertResult(String testName, Boolean expectSuccess) throws InterruptedException {
299         assertTrue("Cert installer did not respond in time.",
300                 mAvailableResultSemaphore.tryAcquire(180, TimeUnit.SECONDS));
301         synchronized (this) {
302             if (expectSuccess) {
303                 assertTrue(testName + " failed unexpectedly.", mReceivedResult);
304                 assertNull(testName + " raised exception", mReceivedException);
305             } else {
306                 assertFalse(testName + " succeeded unexpectedly.", mReceivedResult);
307                 assertTrue(testName + " did not raise SecurityException",
308                         mReceivedException != null &&
309                         mReceivedException instanceof SecurityException);
310             }
311         }
312     }
313 
installKeyPair(String key, String cert, String alias)314     private void installKeyPair(String key, String cert, String alias) {
315         Intent intent = new Intent();
316         intent.setAction(ACTION_INSTALL_KEYPAIR);
317         intent.setComponent(CERT_INSTALLER_COMPONENT);
318         intent.putExtra(EXTRA_CERT_DATA, cert);
319         intent.putExtra(EXTRA_KEY_DATA, key);
320         intent.putExtra(EXTRA_KEY_ALIAS, alias);
321         intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
322         mContext.sendBroadcast(intent);
323     }
324 
readEnrollmentId()325     private void readEnrollmentId() {
326         Intent intent = new Intent();
327         intent.setAction(ACTION_READ_ENROLLMENT_SPECIFIC_ID);
328         intent.setComponent(CERT_INSTALLER_COMPONENT);
329         intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
330         mContext.sendBroadcast(intent);
331     }
332 }
333