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