blob: 998bb681dd24cdbaaa6e677387dd192a5fa1ceeb [file] [log] [blame]
Chad Brubaker5f967022015-11-04 23:55:29 -08001/*
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
17package android.security.net.config;
18
19import android.content.Context;
20import android.test.AndroidTestCase;
21import android.test.MoreAsserts;
22import android.util.ArraySet;
23import android.util.Pair;
24import java.io.IOException;
25import java.net.Socket;
26import java.net.URL;
Chad Brubaker5a1078f2015-11-10 12:26:18 -080027import java.security.KeyStore;
28import java.security.Provider;
29import java.security.Security;
30import java.security.cert.X509Certificate;
Chad Brubaker5f967022015-11-04 23:55:29 -080031import java.util.ArrayList;
32import java.util.Collections;
Chad Brubaker08d36202015-11-09 13:38:51 -080033import java.util.Set;
Chad Brubaker5f967022015-11-04 23:55:29 -080034import javax.net.ssl.HttpsURLConnection;
35import javax.net.ssl.SSLContext;
36import javax.net.ssl.SSLHandshakeException;
37import javax.net.ssl.TrustManager;
Chad Brubaker5a1078f2015-11-10 12:26:18 -080038import javax.net.ssl.TrustManagerFactory;
Chad Brubaker5f967022015-11-04 23:55:29 -080039
40public class XmlConfigTests extends AndroidTestCase {
41
Chad Brubaker08d36202015-11-09 13:38:51 -080042 private final static String DEBUG_CA_SUBJ = "O=AOSP, CN=Test debug CA";
43
Chad Brubaker5f967022015-11-04 23:55:29 -080044 public void testEmptyConfigFile() throws Exception {
45 XmlConfigSource source = new XmlConfigSource(getContext(), R.xml.empty_config);
46 ApplicationConfig appConfig = new ApplicationConfig(source);
47 assertFalse(appConfig.hasPerDomainConfigs());
48 NetworkSecurityConfig config = appConfig.getConfigForHostname("");
49 assertNotNull(config);
50 // Check defaults.
51 assertTrue(config.isCleartextTrafficPermitted());
52 assertFalse(config.isHstsEnforced());
53 assertFalse(config.getTrustAnchors().isEmpty());
54 PinSet pinSet = config.getPins();
55 assertTrue(pinSet.pins.isEmpty());
56 // Try some connections.
57 SSLContext context = TestUtils.getSSLContext(source);
58 TestUtils.assertConnectionSucceeds(context, "android.com", 443);
59 TestUtils.assertConnectionSucceeds(context, "developer.android.com", 443);
60 TestUtils.assertUrlConnectionSucceeds(context, "google.com", 443);
61 }
62
63 public void testEmptyAnchors() throws Exception {
64 XmlConfigSource source = new XmlConfigSource(getContext(), R.xml.empty_trust);
65 ApplicationConfig appConfig = new ApplicationConfig(source);
66 assertFalse(appConfig.hasPerDomainConfigs());
67 NetworkSecurityConfig config = appConfig.getConfigForHostname("");
68 assertNotNull(config);
69 // Check defaults.
70 assertTrue(config.isCleartextTrafficPermitted());
71 assertFalse(config.isHstsEnforced());
72 assertTrue(config.getTrustAnchors().isEmpty());
73 PinSet pinSet = config.getPins();
74 assertTrue(pinSet.pins.isEmpty());
75 SSLContext context = TestUtils.getSSLContext(source);
76 TestUtils.assertConnectionFails(context, "android.com", 443);
77 TestUtils.assertConnectionFails(context, "developer.android.com", 443);
78 TestUtils.assertUrlConnectionFails(context, "google.com", 443);
79 }
80
81 public void testBasicDomainConfig() throws Exception {
82 XmlConfigSource source = new XmlConfigSource(getContext(), R.xml.domain1);
83 ApplicationConfig appConfig = new ApplicationConfig(source);
84 assertTrue(appConfig.hasPerDomainConfigs());
85 NetworkSecurityConfig config = appConfig.getConfigForHostname("");
86 assertNotNull(config);
87 // Check defaults.
88 assertTrue(config.isCleartextTrafficPermitted());
89 assertFalse(config.isHstsEnforced());
90 assertTrue(config.getTrustAnchors().isEmpty());
91 PinSet pinSet = config.getPins();
92 assertTrue(pinSet.pins.isEmpty());
93 // Check android.com.
94 config = appConfig.getConfigForHostname("android.com");
95 assertTrue(config.isCleartextTrafficPermitted());
96 assertFalse(config.isHstsEnforced());
97 assertFalse(config.getTrustAnchors().isEmpty());
98 pinSet = config.getPins();
99 assertTrue(pinSet.pins.isEmpty());
100 // Try connections.
101 SSLContext context = TestUtils.getSSLContext(source);
102 TestUtils.assertConnectionSucceeds(context, "android.com", 443);
103 TestUtils.assertConnectionFails(context, "developer.android.com", 443);
104 TestUtils.assertUrlConnectionFails(context, "google.com", 443);
105 TestUtils.assertUrlConnectionSucceeds(context, "android.com", 443);
106 }
107
108 public void testBasicPinning() throws Exception {
109 XmlConfigSource source = new XmlConfigSource(getContext(), R.xml.pins1);
110 ApplicationConfig appConfig = new ApplicationConfig(source);
111 assertTrue(appConfig.hasPerDomainConfigs());
112 // Check android.com.
113 NetworkSecurityConfig config = appConfig.getConfigForHostname("android.com");
114 PinSet pinSet = config.getPins();
115 assertFalse(pinSet.pins.isEmpty());
116 // Try connections.
117 SSLContext context = TestUtils.getSSLContext(source);
118 TestUtils.assertConnectionSucceeds(context, "android.com", 443);
119 TestUtils.assertUrlConnectionSucceeds(context, "android.com", 443);
120 TestUtils.assertConnectionSucceeds(context, "google.com", 443);
121 }
122
123 public void testExpiredPin() throws Exception {
124 XmlConfigSource source = new XmlConfigSource(getContext(), R.xml.expired_pin);
125 ApplicationConfig appConfig = new ApplicationConfig(source);
126 assertTrue(appConfig.hasPerDomainConfigs());
127 // Check android.com.
128 NetworkSecurityConfig config = appConfig.getConfigForHostname("android.com");
129 PinSet pinSet = config.getPins();
130 assertFalse(pinSet.pins.isEmpty());
131 // Try connections.
132 SSLContext context = TestUtils.getSSLContext(source);
133 TestUtils.assertConnectionSucceeds(context, "android.com", 443);
134 TestUtils.assertUrlConnectionSucceeds(context, "android.com", 443);
135 }
136
137 public void testOverridesPins() throws Exception {
138 XmlConfigSource source = new XmlConfigSource(getContext(), R.xml.override_pins);
139 ApplicationConfig appConfig = new ApplicationConfig(source);
140 assertTrue(appConfig.hasPerDomainConfigs());
141 // Check android.com.
142 NetworkSecurityConfig config = appConfig.getConfigForHostname("android.com");
143 PinSet pinSet = config.getPins();
144 assertFalse(pinSet.pins.isEmpty());
145 // Try connections.
146 SSLContext context = TestUtils.getSSLContext(source);
147 TestUtils.assertConnectionSucceeds(context, "android.com", 443);
148 TestUtils.assertUrlConnectionSucceeds(context, "android.com", 443);
149 }
150
151 public void testBadPin() throws Exception {
152 XmlConfigSource source = new XmlConfigSource(getContext(), R.xml.bad_pin);
153 ApplicationConfig appConfig = new ApplicationConfig(source);
154 assertTrue(appConfig.hasPerDomainConfigs());
155 // Check android.com.
156 NetworkSecurityConfig config = appConfig.getConfigForHostname("android.com");
157 PinSet pinSet = config.getPins();
158 assertFalse(pinSet.pins.isEmpty());
159 // Try connections.
160 SSLContext context = TestUtils.getSSLContext(source);
161 TestUtils.assertConnectionFails(context, "android.com", 443);
162 TestUtils.assertUrlConnectionFails(context, "android.com", 443);
163 TestUtils.assertConnectionSucceeds(context, "google.com", 443);
164 }
165
166 public void testMultipleDomains() throws Exception {
167 XmlConfigSource source = new XmlConfigSource(getContext(), R.xml.multiple_domains);
168 ApplicationConfig appConfig = new ApplicationConfig(source);
169 assertTrue(appConfig.hasPerDomainConfigs());
170 NetworkSecurityConfig config = appConfig.getConfigForHostname("android.com");
171 assertTrue(config.isCleartextTrafficPermitted());
172 assertFalse(config.isHstsEnforced());
173 assertFalse(config.getTrustAnchors().isEmpty());
174 PinSet pinSet = config.getPins();
175 assertTrue(pinSet.pins.isEmpty());
176 // Both android.com and google.com should use the same config
177 NetworkSecurityConfig other = appConfig.getConfigForHostname("google.com");
178 assertEquals(config, other);
179 // Try connections.
180 SSLContext context = TestUtils.getSSLContext(source);
181 TestUtils.assertConnectionSucceeds(context, "android.com", 443);
182 TestUtils.assertConnectionSucceeds(context, "google.com", 443);
183 TestUtils.assertConnectionFails(context, "developer.android.com", 443);
184 TestUtils.assertUrlConnectionSucceeds(context, "android.com", 443);
185 }
186
187 public void testMultipleDomainConfigs() throws Exception {
188 XmlConfigSource source = new XmlConfigSource(getContext(), R.xml.multiple_configs);
189 ApplicationConfig appConfig = new ApplicationConfig(source);
190 assertTrue(appConfig.hasPerDomainConfigs());
191 // Should be two different config objects
192 NetworkSecurityConfig config = appConfig.getConfigForHostname("android.com");
193 NetworkSecurityConfig other = appConfig.getConfigForHostname("google.com");
194 MoreAsserts.assertNotEqual(config, other);
195 // Try connections.
196 SSLContext context = TestUtils.getSSLContext(source);
197 TestUtils.assertConnectionSucceeds(context, "android.com", 443);
198 TestUtils.assertConnectionSucceeds(context, "google.com", 443);
199 TestUtils.assertUrlConnectionSucceeds(context, "android.com", 443);
200 }
201
202 public void testIncludeSubdomains() throws Exception {
203 XmlConfigSource source = new XmlConfigSource(getContext(), R.xml.subdomains);
204 ApplicationConfig appConfig = new ApplicationConfig(source);
205 assertTrue(appConfig.hasPerDomainConfigs());
206 // Try connections.
207 SSLContext context = TestUtils.getSSLContext(source);
208 TestUtils.assertConnectionSucceeds(context, "android.com", 443);
209 TestUtils.assertConnectionSucceeds(context, "developer.android.com", 443);
210 TestUtils.assertUrlConnectionSucceeds(context, "android.com", 443);
211 TestUtils.assertUrlConnectionSucceeds(context, "developer.android.com", 443);
212 TestUtils.assertConnectionFails(context, "google.com", 443);
213 }
214
215 public void testAttributes() throws Exception {
216 XmlConfigSource source = new XmlConfigSource(getContext(), R.xml.attributes);
217 ApplicationConfig appConfig = new ApplicationConfig(source);
218 assertFalse(appConfig.hasPerDomainConfigs());
219 NetworkSecurityConfig config = appConfig.getConfigForHostname("");
220 assertTrue(config.isHstsEnforced());
221 assertFalse(config.isCleartextTrafficPermitted());
222 }
223
224 public void testResourcePemCertificateSource() throws Exception {
225 XmlConfigSource source = new XmlConfigSource(getContext(), R.xml.resource_anchors_pem);
226 ApplicationConfig appConfig = new ApplicationConfig(source);
227 // Check android.com.
228 NetworkSecurityConfig config = appConfig.getConfigForHostname("android.com");
229 assertTrue(config.isCleartextTrafficPermitted());
230 assertFalse(config.isHstsEnforced());
231 assertEquals(2, config.getTrustAnchors().size());
232 // Try connections.
233 SSLContext context = TestUtils.getSSLContext(source);
234 TestUtils.assertConnectionSucceeds(context, "android.com", 443);
235 TestUtils.assertConnectionFails(context, "developer.android.com", 443);
236 TestUtils.assertUrlConnectionFails(context, "google.com", 443);
237 TestUtils.assertUrlConnectionSucceeds(context, "android.com", 443);
238 }
239
240 public void testResourceDerCertificateSource() throws Exception {
241 XmlConfigSource source = new XmlConfigSource(getContext(), R.xml.resource_anchors_der);
242 ApplicationConfig appConfig = new ApplicationConfig(source);
243 // Check android.com.
244 NetworkSecurityConfig config = appConfig.getConfigForHostname("android.com");
245 assertTrue(config.isCleartextTrafficPermitted());
246 assertFalse(config.isHstsEnforced());
247 assertEquals(2, config.getTrustAnchors().size());
248 // Try connections.
249 SSLContext context = TestUtils.getSSLContext(source);
250 TestUtils.assertConnectionSucceeds(context, "android.com", 443);
251 TestUtils.assertConnectionFails(context, "developer.android.com", 443);
252 TestUtils.assertUrlConnectionFails(context, "google.com", 443);
253 TestUtils.assertUrlConnectionSucceeds(context, "android.com", 443);
254 }
255
Chad Brubakerbd173c22015-11-06 23:02:37 -0800256 public void testNestedDomainConfigs() throws Exception {
257 XmlConfigSource source = new XmlConfigSource(getContext(), R.xml.nested_domains);
258 ApplicationConfig appConfig = new ApplicationConfig(source);
259 assertTrue(appConfig.hasPerDomainConfigs());
260 NetworkSecurityConfig parent = appConfig.getConfigForHostname("android.com");
261 NetworkSecurityConfig child = appConfig.getConfigForHostname("developer.android.com");
262 MoreAsserts.assertNotEqual(parent, child);
263 MoreAsserts.assertEmpty(parent.getPins().pins);
264 MoreAsserts.assertNotEmpty(child.getPins().pins);
265 // Check that the child inherited the cleartext value and anchors.
266 assertFalse(child.isCleartextTrafficPermitted());
267 MoreAsserts.assertNotEmpty(child.getTrustAnchors());
268 // Test connections.
269 SSLContext context = TestUtils.getSSLContext(source);
270 TestUtils.assertConnectionSucceeds(context, "android.com", 443);
271 TestUtils.assertConnectionSucceeds(context, "developer.android.com", 443);
272 }
273
274 public void testNestedDomainConfigsOverride() throws Exception {
275 XmlConfigSource source = new XmlConfigSource(getContext(), R.xml.nested_domains_override);
276 ApplicationConfig appConfig = new ApplicationConfig(source);
277 assertTrue(appConfig.hasPerDomainConfigs());
278 NetworkSecurityConfig parent = appConfig.getConfigForHostname("android.com");
279 NetworkSecurityConfig child = appConfig.getConfigForHostname("developer.android.com");
280 MoreAsserts.assertNotEqual(parent, child);
281 assertTrue(parent.isCleartextTrafficPermitted());
282 assertFalse(child.isCleartextTrafficPermitted());
283 }
284
Chad Brubaker08d36202015-11-09 13:38:51 -0800285 public void testDebugOverridesDisabled() throws Exception {
286 XmlConfigSource source = new XmlConfigSource(getContext(), R.xml.debug_basic, false);
287 ApplicationConfig appConfig = new ApplicationConfig(source);
288 NetworkSecurityConfig config = appConfig.getConfigForHostname("");
289 Set<TrustAnchor> anchors = config.getTrustAnchors();
290 MoreAsserts.assertEmpty(anchors);
291 SSLContext context = TestUtils.getSSLContext(source);
292 TestUtils.assertConnectionFails(context, "android.com", 443);
293 TestUtils.assertConnectionFails(context, "developer.android.com", 443);
294 }
295
296 public void testBasicDebugOverrides() throws Exception {
297 XmlConfigSource source = new XmlConfigSource(getContext(), R.xml.debug_basic, true);
298 ApplicationConfig appConfig = new ApplicationConfig(source);
299 NetworkSecurityConfig config = appConfig.getConfigForHostname("");
300 Set<TrustAnchor> anchors = config.getTrustAnchors();
301 MoreAsserts.assertNotEmpty(anchors);
302 for (TrustAnchor anchor : anchors) {
303 assertTrue(anchor.overridesPins);
304 }
305 SSLContext context = TestUtils.getSSLContext(source);
306 TestUtils.assertConnectionSucceeds(context, "android.com", 443);
307 TestUtils.assertConnectionSucceeds(context, "developer.android.com", 443);
308 }
309
310 public void testDebugOverridesWithDomain() throws Exception {
311 XmlConfigSource source = new XmlConfigSource(getContext(), R.xml.debug_domain, true);
312 ApplicationConfig appConfig = new ApplicationConfig(source);
313 NetworkSecurityConfig config = appConfig.getConfigForHostname("android.com");
314 Set<TrustAnchor> anchors = config.getTrustAnchors();
315 boolean foundDebugCA = false;
316 for (TrustAnchor anchor : anchors) {
317 if (anchor.certificate.getSubjectDN().toString().equals(DEBUG_CA_SUBJ)) {
318 foundDebugCA = true;
319 assertTrue(anchor.overridesPins);
320 }
321 }
322 assertTrue(foundDebugCA);
323 SSLContext context = TestUtils.getSSLContext(source);
324 TestUtils.assertConnectionSucceeds(context, "android.com", 443);
325 TestUtils.assertConnectionSucceeds(context, "developer.android.com", 443);
326 }
327
328 public void testDebugInherit() throws Exception {
329 XmlConfigSource source = new XmlConfigSource(getContext(), R.xml.debug_domain, true);
330 ApplicationConfig appConfig = new ApplicationConfig(source);
331 NetworkSecurityConfig config = appConfig.getConfigForHostname("android.com");
332 Set<TrustAnchor> anchors = config.getTrustAnchors();
333 boolean foundDebugCA = false;
334 for (TrustAnchor anchor : anchors) {
335 if (anchor.certificate.getSubjectDN().toString().equals(DEBUG_CA_SUBJ)) {
336 foundDebugCA = true;
337 assertTrue(anchor.overridesPins);
338 }
339 }
340 assertTrue(foundDebugCA);
341 assertTrue(anchors.size() > 1);
342 SSLContext context = TestUtils.getSSLContext(source);
343 TestUtils.assertConnectionSucceeds(context, "android.com", 443);
344 TestUtils.assertConnectionSucceeds(context, "developer.android.com", 443);
345 }
346
Chad Brubaker5f967022015-11-04 23:55:29 -0800347 private void testBadConfig(int configId) throws Exception {
348 try {
349 XmlConfigSource source = new XmlConfigSource(getContext(), configId);
350 ApplicationConfig appConfig = new ApplicationConfig(source);
351 appConfig.getConfigForHostname("android.com");
352 fail("Bad config " + getContext().getResources().getResourceName(configId)
353 + " did not fail to parse");
354 } catch (RuntimeException e) {
355 MoreAsserts.assertAssignableFrom(XmlConfigSource.ParserException.class,
356 e.getCause());
357 }
358 }
359
360 public void testBadConfig0() throws Exception {
361 testBadConfig(R.xml.bad_config0);
362 }
363
364 public void testBadConfig1() throws Exception {
365 testBadConfig(R.xml.bad_config1);
366 }
367
368 public void testBadConfig2() throws Exception {
369 testBadConfig(R.xml.bad_config2);
370 }
371
372 public void testBadConfig3() throws Exception {
373 testBadConfig(R.xml.bad_config3);
374 }
375
376 public void testBadConfig4() throws Exception {
377 testBadConfig(R.xml.bad_config4);
378 }
379
380 public void testBadConfig5() throws Exception {
381 testBadConfig(R.xml.bad_config4);
382 }
Chad Brubaker5a1078f2015-11-10 12:26:18 -0800383
384 public void testTrustManagerKeystore() throws Exception {
385 XmlConfigSource source = new XmlConfigSource(getContext(), R.xml.bad_pin, true);
386 ApplicationConfig appConfig = new ApplicationConfig(source);
387 Provider provider = new NetworkSecurityConfigProvider();
388 TrustManagerFactory tmf =
389 TrustManagerFactory.getInstance("PKIX", provider);
390 KeyStore keystore = KeyStore.getInstance(KeyStore.getDefaultType());
391 keystore.load(null);
392 int i = 0;
393 for (X509Certificate cert : SystemCertificateSource.getInstance().getCertificates()) {
394 keystore.setEntry(String.valueOf(i),
395 new KeyStore.TrustedCertificateEntry(cert),
396 null);
397 i++;
398 }
399 tmf.init(keystore);
400 TrustManager[] tms = tmf.getTrustManagers();
401 SSLContext context = SSLContext.getInstance("TLS");
402 context.init(null, tms, null);
403 TestUtils.assertConnectionSucceeds(context, "android.com" , 443);
404 }
Chad Brubakerd3af9622015-11-16 10:48:20 -0800405
406 public void testDebugDedup() throws Exception {
407 XmlConfigSource source = new XmlConfigSource(getContext(), R.xml.override_dedup, true);
408 ApplicationConfig appConfig = new ApplicationConfig(source);
409 assertTrue(appConfig.hasPerDomainConfigs());
410 // Check android.com.
411 NetworkSecurityConfig config = appConfig.getConfigForHostname("android.com");
412 PinSet pinSet = config.getPins();
413 assertFalse(pinSet.pins.isEmpty());
414 // Check that all TrustAnchors come from the override pins debug source.
415 for (TrustAnchor anchor : config.getTrustAnchors()) {
416 assertTrue(anchor.overridesPins);
417 }
418 // Try connections.
419 SSLContext context = TestUtils.getSSLContext(source);
420 TestUtils.assertConnectionSucceeds(context, "android.com", 443);
421 TestUtils.assertUrlConnectionSucceeds(context, "android.com", 443);
422 }
Chad Brubaker5f967022015-11-04 23:55:29 -0800423}