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