1 /* 2 * Copyright (C) 2017 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 package com.android.cts.delegate; 17 18 import static android.app.admin.DevicePolicyManager.DELEGATION_CERT_INSTALL; 19 20 import android.app.admin.DevicePolicyManager; 21 import android.content.BroadcastReceiver; 22 import android.content.ComponentName; 23 import android.content.Context; 24 import android.content.Intent; 25 import android.content.IntentFilter; 26 import android.os.Bundle; 27 import android.os.Parcelable; 28 import android.os.UserManager; 29 import android.test.InstrumentationTestCase; 30 import android.test.MoreAsserts; 31 import android.util.Base64; 32 import android.util.Base64InputStream; 33 import android.util.Log; 34 35 import java.io.ByteArrayInputStream; 36 import java.security.cert.Certificate; 37 import java.security.cert.CertificateException; 38 import java.security.cert.CertificateFactory; 39 import java.security.spec.PKCS8EncodedKeySpec; 40 import java.security.KeyFactory; 41 import java.security.PrivateKey; 42 import java.util.List; 43 import java.util.concurrent.Semaphore; 44 import java.util.concurrent.TimeUnit; 45 46 /** 47 * Tests that a package other than the DPC can manage app restrictions if allowed by the DPC 48 * via {@link DevicePolicyManager#setApplicationRestrictionsManagingPackage(ComponentName, String)} 49 */ 50 public class CertInstallDelegateTest extends InstrumentationTestCase { 51 52 private static final String TEST_CA = 53 "-----BEGIN CERTIFICATE-----\n" + 54 "MIIDXTCCAkWgAwIBAgIJAK9Tl/F9V8kSMA0GCSqGSIb3DQEBCwUAMEUxCzAJBgNV\n" + 55 "BAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBX\n" + 56 "aWRnaXRzIFB0eSBMdGQwHhcNMTUwMzA2MTczMjExWhcNMjUwMzAzMTczMjExWjBF\n" + 57 "MQswCQYDVQQGEwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50\n" + 58 "ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB\n" + 59 "CgKCAQEAvItOutsE75WBTgTyNAHt4JXQ3JoseaGqcC3WQij6vhrleWi5KJ0jh1/M\n" + 60 "Rpry7Fajtwwb4t8VZa0NuM2h2YALv52w1xivql88zce/HU1y7XzbXhxis9o6SCI+\n" + 61 "oVQSbPeXRgBPppFzBEh3ZqYTVhAqw451XhwdA4Aqs3wts7ddjwlUzyMdU44osCUg\n" + 62 "kVg7lfPf9sTm5IoHVcfLSCWH5n6Nr9sH3o2ksyTwxuOAvsN11F/a0mmUoPciYPp+\n" + 63 "q7DzQzdi7akRG601DZ4YVOwo6UITGvDyuAAdxl5isovUXqe6Jmz2/myTSpAKxGFs\n" + 64 "jk9oRoG6WXWB1kni490GIPjJ1OceyQIDAQABo1AwTjAdBgNVHQ4EFgQUH1QIlPKL\n" + 65 "p2OQ/AoLOjKvBW4zK3AwHwYDVR0jBBgwFoAUH1QIlPKLp2OQ/AoLOjKvBW4zK3Aw\n" + 66 "DAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAcMi4voMMJHeQLjtq8Oky\n" + 67 "Azpyk8moDwgCd4llcGj7izOkIIFqq/lyqKdtykVKUWz2bSHO5cLrtaOCiBWVlaCV\n" + 68 "DYAnnVLM8aqaA6hJDIfaGs4zmwz0dY8hVMFCuCBiLWuPfiYtbEmjHGSmpQTG6Qxn\n" + 69 "ZJlaK5CZyt5pgh5EdNdvQmDEbKGmu0wpCq9qjZImwdyAul1t/B0DrsWApZMgZpeI\n" + 70 "d2od0VBrCICB1K4p+C51D93xyQiva7xQcCne+TAnGNy9+gjQ/MyR8MRpwRLv5ikD\n" + 71 "u0anJCN8pXo6IMglfMAsoton1J6o5/ae5uhC6caQU8bNUsCK570gpNfjkzo6rbP0\n" + 72 "wQ==\n" + 73 "-----END CERTIFICATE-----"; 74 75 // Content from userkey.pem without the private key header and footer. 76 private static final String TEST_KEY = 77 "MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBALCYprGsTU+5L3KM\n" + 78 "fhkm0gXM2xjGUH+543YLiMPGVr3eVS7biue1/tQlL+fJsw3rqsPKJe71RbVWlpqU\n" + 79 "mhegxG4s3IvGYVB0KZoRIjDKmnnvlx6nngL2ZJ8O27U42pHsw4z4MKlcQlWkjL3T\n" + 80 "9sV6zW2Wzri+f5mvzKjhnArbLktHAgMBAAECgYBlfVVPhtZnmuXJzzQpAEZzTugb\n" + 81 "tN1OimZO0RIocTQoqj4KT+HkiJOLGFQPwbtFpMre+q4SRqNpM/oZnI1yRtKcCmIc\n" + 82 "mZgkwJ2k6pdSxqO0ofxFFTdT9czJ3rCnqBHy1g6BqUQFXT4olcygkxUpKYUwzlz1\n" + 83 "oAl487CoPxyr4sVEAQJBANwiUOHcdGd2RoRILDzw5WOXWBoWPOKzX/K9wt0yL+mO\n" + 84 "wlFNFSymqo9eLheHcEq/VD9qK9rT700dCewJfWj6+bECQQDNXmWNYIxGii5NJilT\n" + 85 "OBOHiMD/F0NE178j+/kmacbhDJwpkbLYXaP8rW4+Iswrm4ORJ59lvjNuXaZ28+sx\n" + 86 "fFp3AkA6Z7Bl/IO135+eATgbgx6ZadIqObQ1wbm3Qbmtzl7/7KyJvZXcnuup1icM\n" + 87 "fxa//jtwB89S4+Ad6ZJ0WaA4dj5BAkEAuG7V9KmIULE388EZy8rIfyepa22Q0/qN\n" + 88 "hdt8XasRGHsio5Jdc0JlSz7ViqflhCQde/aBh/XQaoVgQeO8jKyI8QJBAJHekZDj\n" + 89 "WA0w1RsBVVReN1dVXgjm1CykeAT8Qx8TUmBUfiDX6w6+eGQjKtS7f4KC2IdRTV6+\n" + 90 "bDzDoHBChHNC9ms=\n"; 91 92 // Content from usercert.pem without the header and footer. 93 private static final String TEST_CERT = 94 "MIIDEjCCAfqgAwIBAgIBATANBgkqhkiG9w0BAQsFADBFMQswCQYDVQQGEwJBVTET\n" + 95 "MBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0cyBQ\n" + 96 "dHkgTHRkMB4XDTE1MDUwMTE2NTQwNVoXDTI1MDQyODE2NTQwNVowWzELMAkGA1UE\n" + 97 "BhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGEludGVybmV0IFdp\n" + 98 "ZGdpdHMgUHR5IEx0ZDEUMBIGA1UEAwwLY2xpZW50IGNlcnQwgZ8wDQYJKoZIhvcN\n" + 99 "AQEBBQADgY0AMIGJAoGBALCYprGsTU+5L3KMfhkm0gXM2xjGUH+543YLiMPGVr3e\n" + 100 "VS7biue1/tQlL+fJsw3rqsPKJe71RbVWlpqUmhegxG4s3IvGYVB0KZoRIjDKmnnv\n" + 101 "lx6nngL2ZJ8O27U42pHsw4z4MKlcQlWkjL3T9sV6zW2Wzri+f5mvzKjhnArbLktH\n" + 102 "AgMBAAGjezB5MAkGA1UdEwQCMAAwLAYJYIZIAYb4QgENBB8WHU9wZW5TU0wgR2Vu\n" + 103 "ZXJhdGVkIENlcnRpZmljYXRlMB0GA1UdDgQWBBQ8GL+jKSarvTn9fVNA2AzjY7qq\n" + 104 "gjAfBgNVHSMEGDAWgBRzBBA5sNWyT/fK8GrhN3tOqO5tgjANBgkqhkiG9w0BAQsF\n" + 105 "AAOCAQEAgwQEd2bktIDZZi/UOwU1jJUgGq7NiuBDPHcqgzjxhGFLQ8SQAAP3v3PR\n" + 106 "mLzcfxsxnzGynqN5iHQT4rYXxxaqrp1iIdj9xl9Wl5FxjZgXITxhlRscOd/UOBvG\n" + 107 "oMrazVczjjdoRIFFnjtU3Jf0Mich68HD1Z0S3o7X6sDYh6FTVR5KbLcxbk6RcoG4\n" + 108 "VCI5boR5LUXgb5Ed5UxczxvN12S71fyxHYVpuuI0z0HTIbAxKeRw43I6HWOmR1/0\n" + 109 "G6byGCNL/1Fz7Y+264fGqABSNTKdZwIU2K4ANEH7F+9scnhoO6OBp+gjBe5O+7jb\n" + 110 "wZmUCAoTka4hmoaOCj7cqt/IkmxozQ==\n"; 111 112 private DevicePolicyManager mDpm; 113 114 @Override setUp()115 protected void setUp() throws Exception { 116 super.setUp(); 117 118 Context context = getInstrumentation().getContext(); 119 mDpm = context.getSystemService(DevicePolicyManager.class); 120 } 121 testCannotAccessApis()122 public void testCannotAccessApis() { 123 assertFalse(amICertInstallDelegate()); 124 try { 125 mDpm.installCaCert(null, null); 126 fail("Expected SecurityException not thrown"); 127 } catch (SecurityException expected) { 128 MoreAsserts.assertContainsRegex( 129 "Neither user \\d+ nor current process has " 130 + "android.permission.MANAGE_CA_CERTIFICATES", 131 expected.getMessage()); 132 } 133 try { 134 mDpm.removeKeyPair(null, "alias"); 135 fail("Expected SecurityException not thrown"); 136 } catch (SecurityException expected) { 137 MoreAsserts.assertContainsRegex( 138 "Caller with uid \\d+ is not a delegate of scope delegation-cert-install.", 139 expected.getMessage()); 140 } 141 } 142 testCanAccessApis()143 public void testCanAccessApis() throws Exception { 144 assertTrue(amICertInstallDelegate()); 145 146 byte[] cert = TEST_CA.getBytes(); 147 148 // Exercise installCaCert. 149 assertTrue("Certificate installation failed", mDpm.installCaCert(null, cert)); 150 151 // Exercise hasCertInstalled. 152 assertTrue("Certificate is not installed properly", mDpm.hasCaCertInstalled(null, cert)); 153 154 // Exercise getInstalledCaCerts. 155 assertTrue("Certificate is not among installed certs", 156 containsCertificate(mDpm.getInstalledCaCerts(null), cert)); 157 158 // Exercise uninstallCaCert. 159 mDpm.uninstallCaCert(null, cert); 160 assertFalse("Certificate was not uninstalled", mDpm.hasCaCertInstalled(null, cert)); 161 162 // Exercise installKeyPair. 163 final String alias = "delegated-cert-installer-test-key"; 164 165 PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec( 166 Base64.decode(TEST_KEY, Base64.DEFAULT)); 167 KeyFactory kf = KeyFactory.getInstance("RSA"); 168 PrivateKey privatekey = kf.generatePrivate(keySpec); 169 170 Certificate certificate = CertificateFactory.getInstance("X.509").generateCertificate( 171 new Base64InputStream(new ByteArrayInputStream(TEST_CERT.getBytes()), 172 Base64.DEFAULT)); 173 assertTrue("Key pair not installed successfully", 174 mDpm.installKeyPair(null, privatekey, certificate, alias)); 175 176 // Exercise removeKeyPair. 177 assertTrue("Key pair not removed successfully", mDpm.removeKeyPair(null, alias)); 178 } 179 containsCertificate(List<byte[]> certificates, byte[] toMatch)180 private static boolean containsCertificate(List<byte[]> certificates, byte[] toMatch) 181 throws CertificateException { 182 Certificate certificateToMatch = readCertificate(toMatch); 183 for (byte[] certBuffer : certificates) { 184 Certificate cert = readCertificate(certBuffer); 185 if (certificateToMatch.equals(cert)) { 186 return true; 187 } 188 } 189 return false; 190 } 191 readCertificate(byte[] certBuffer)192 private static Certificate readCertificate(byte[] certBuffer) throws CertificateException { 193 final CertificateFactory certFactory = CertificateFactory.getInstance("X.509"); 194 return certFactory.generateCertificate(new ByteArrayInputStream(certBuffer)); 195 } 196 amICertInstallDelegate()197 private boolean amICertInstallDelegate() { 198 final String packageName = getInstrumentation().getContext().getPackageName(); 199 final List<String> scopes = mDpm.getDelegatedScopes(null, packageName); 200 return scopes.contains(DELEGATION_CERT_INSTALL); 201 } 202 } 203