Loading core/java/android/security/net/config/XmlConfigSource.java +26 −8 Original line number Diff line number Diff line Loading @@ -186,15 +186,21 @@ public class XmlConfigSource implements ConfigSource { return anchors; } private Pair<NetworkSecurityConfig.Builder, Set<Domain>> parseConfigEntry( XmlResourceParser parser, Set<String> seenDomains, boolean baseConfig) private List<Pair<NetworkSecurityConfig.Builder, Set<Domain>>> parseConfigEntry( XmlResourceParser parser, Set<String> seenDomains, NetworkSecurityConfig.Builder parentBuilder, boolean baseConfig) throws IOException, XmlPullParserException, ParserException { List<Pair<NetworkSecurityConfig.Builder, Set<Domain>>> builders = new ArrayList<>(); NetworkSecurityConfig.Builder builder = new NetworkSecurityConfig.Builder(); builder.setParent(parentBuilder); Set<Domain> domains = new ArraySet<>(); boolean seenPinSet = false; boolean seenTrustAnchors = false; String configName = parser.getName(); int outerDepth = parser.getDepth(); // Add this builder now so that this builder occurs before any of its children. This // makes the final build pass easier. builders.add(new Pair<>(builder, domains)); // Parse config attributes. Only set values that are present, config inheritence will // handle the rest. for (int i = 0; i < parser.getAttributeCount(); i++) { Loading @@ -212,7 +218,6 @@ public class XmlConfigSource implements ConfigSource { // Parse the config elements. while (XmlUtils.nextElementWithin(parser, outerDepth)) { String tagName = parser.getName(); // TODO: Support nested domain-config entries. if ("domain".equals(tagName)) { if (baseConfig) { throw new ParserException(parser, "domain element not allowed in base-config"); Loading @@ -236,6 +241,12 @@ public class XmlConfigSource implements ConfigSource { } builder.setPinSet(parsePinSet(parser)); seenPinSet = true; } else if ("domain-config".equals(tagName)) { if (baseConfig) { throw new ParserException(parser, "Nested domain-config not allowed in base-config"); } builders.addAll(parseConfigEntry(parser, seenDomains, builder, false)); } else { XmlUtils.skipCurrentTag(parser); } Loading @@ -243,7 +254,7 @@ public class XmlConfigSource implements ConfigSource { if (!baseConfig && domains.isEmpty()) { throw new ParserException(parser, "No domain elements in domain-config"); } return new Pair<>(builder, domains); return builders; } private void parseNetworkSecurityConfig(XmlResourceParser parser) Loading @@ -263,9 +274,9 @@ public class XmlConfigSource implements ConfigSource { throw new ParserException(parser, "Only one base-config allowed"); } seenBaseConfig = true; baseConfigBuilder = parseConfigEntry(parser, seenDomains, true).first; baseConfigBuilder = parseConfigEntry(parser, seenDomains, null, true).get(0).first; } else if ("domain-config".equals(parser.getName())) { builders.add(parseConfigEntry(parser, seenDomains, false)); builders.addAll(parseConfigEntry(parser, seenDomains, baseConfigBuilder, false)); } else { XmlUtils.skipCurrentTag(parser); } Loading @@ -286,8 +297,15 @@ public class XmlConfigSource implements ConfigSource { for (Pair<NetworkSecurityConfig.Builder, Set<Domain>> entry : builders) { NetworkSecurityConfig.Builder builder = entry.first; Set<Domain> domains = entry.second; // Use the base-config for inheriting any unset values in the domain-config entry. // Set the parent of configs that do not have a parent to the base-config. This can // happen if the base-config comes after a domain-config in the file. // Note that this is safe with regards to children because of the order that // parseConfigEntry returns builders, the parent is always before the children. The // children builders will not have build called until _after_ their parents have their // parent set so everything is consistent. if (builder.getParent() == null) { builder.setParent(baseConfigBuilder); } NetworkSecurityConfig config = builder.build(); for (Domain domain : domains) { configs.add(new Pair<>(domain, config)); Loading tests/NetworkSecurityConfigTest/res/xml/nested_domains.xml 0 → 100644 +18 −0 Original line number Diff line number Diff line <?xml version="1.0" encoding="utf-8"?> <network-security-config> <domain-config> <domain includeSubdomains="true">android.com</domain> <trust-anchors> <certificates src="system" /> </trust-anchors> <!-- nested config that adds pins --> <domain-config> <domain>developer.android.com</domain> <pin-set> <pin digest="SHA-256">7HIpactkIAq2Y49orFOOQKurWxmmSFZhBCoQYcRhJ3Y=</pin> </pin-set> </domain-config> </domain-config> <base-config cleartextTrafficPermitted="false"> </base-config> </network-security-config> tests/NetworkSecurityConfigTest/res/xml/nested_domains_override.xml 0 → 100644 +12 −0 Original line number Diff line number Diff line <?xml version="1.0" encoding="utf-8"?> <network-security-config> <base-config cleartextTrafficPermitted="false"> </base-config> <!-- Nested config that overrides parent --> <domain-config cleartextTrafficPermitted="true"> <domain includeSubdomains="true">android.com</domain> <domain-config cleartextTrafficPermitted="false"> <domain>developer.android.com</domain> </domain-config> </domain-config> </network-security-config> tests/NetworkSecurityConfigTest/src/android/security/net/config/XmlConfigTests.java +29 −0 Original line number Diff line number Diff line Loading @@ -245,6 +245,35 @@ public class XmlConfigTests extends AndroidTestCase { TestUtils.assertUrlConnectionSucceeds(context, "android.com", 443); } public void testNestedDomainConfigs() throws Exception { XmlConfigSource source = new XmlConfigSource(getContext(), R.xml.nested_domains); ApplicationConfig appConfig = new ApplicationConfig(source); assertTrue(appConfig.hasPerDomainConfigs()); NetworkSecurityConfig parent = appConfig.getConfigForHostname("android.com"); NetworkSecurityConfig child = appConfig.getConfigForHostname("developer.android.com"); MoreAsserts.assertNotEqual(parent, child); MoreAsserts.assertEmpty(parent.getPins().pins); MoreAsserts.assertNotEmpty(child.getPins().pins); // Check that the child inherited the cleartext value and anchors. assertFalse(child.isCleartextTrafficPermitted()); MoreAsserts.assertNotEmpty(child.getTrustAnchors()); // Test connections. SSLContext context = TestUtils.getSSLContext(source); TestUtils.assertConnectionSucceeds(context, "android.com", 443); TestUtils.assertConnectionSucceeds(context, "developer.android.com", 443); } public void testNestedDomainConfigsOverride() throws Exception { XmlConfigSource source = new XmlConfigSource(getContext(), R.xml.nested_domains_override); ApplicationConfig appConfig = new ApplicationConfig(source); assertTrue(appConfig.hasPerDomainConfigs()); NetworkSecurityConfig parent = appConfig.getConfigForHostname("android.com"); NetworkSecurityConfig child = appConfig.getConfigForHostname("developer.android.com"); MoreAsserts.assertNotEqual(parent, child); assertTrue(parent.isCleartextTrafficPermitted()); assertFalse(child.isCleartextTrafficPermitted()); } private void testBadConfig(int configId) throws Exception { try { XmlConfigSource source = new XmlConfigSource(getContext(), configId); Loading Loading
core/java/android/security/net/config/XmlConfigSource.java +26 −8 Original line number Diff line number Diff line Loading @@ -186,15 +186,21 @@ public class XmlConfigSource implements ConfigSource { return anchors; } private Pair<NetworkSecurityConfig.Builder, Set<Domain>> parseConfigEntry( XmlResourceParser parser, Set<String> seenDomains, boolean baseConfig) private List<Pair<NetworkSecurityConfig.Builder, Set<Domain>>> parseConfigEntry( XmlResourceParser parser, Set<String> seenDomains, NetworkSecurityConfig.Builder parentBuilder, boolean baseConfig) throws IOException, XmlPullParserException, ParserException { List<Pair<NetworkSecurityConfig.Builder, Set<Domain>>> builders = new ArrayList<>(); NetworkSecurityConfig.Builder builder = new NetworkSecurityConfig.Builder(); builder.setParent(parentBuilder); Set<Domain> domains = new ArraySet<>(); boolean seenPinSet = false; boolean seenTrustAnchors = false; String configName = parser.getName(); int outerDepth = parser.getDepth(); // Add this builder now so that this builder occurs before any of its children. This // makes the final build pass easier. builders.add(new Pair<>(builder, domains)); // Parse config attributes. Only set values that are present, config inheritence will // handle the rest. for (int i = 0; i < parser.getAttributeCount(); i++) { Loading @@ -212,7 +218,6 @@ public class XmlConfigSource implements ConfigSource { // Parse the config elements. while (XmlUtils.nextElementWithin(parser, outerDepth)) { String tagName = parser.getName(); // TODO: Support nested domain-config entries. if ("domain".equals(tagName)) { if (baseConfig) { throw new ParserException(parser, "domain element not allowed in base-config"); Loading @@ -236,6 +241,12 @@ public class XmlConfigSource implements ConfigSource { } builder.setPinSet(parsePinSet(parser)); seenPinSet = true; } else if ("domain-config".equals(tagName)) { if (baseConfig) { throw new ParserException(parser, "Nested domain-config not allowed in base-config"); } builders.addAll(parseConfigEntry(parser, seenDomains, builder, false)); } else { XmlUtils.skipCurrentTag(parser); } Loading @@ -243,7 +254,7 @@ public class XmlConfigSource implements ConfigSource { if (!baseConfig && domains.isEmpty()) { throw new ParserException(parser, "No domain elements in domain-config"); } return new Pair<>(builder, domains); return builders; } private void parseNetworkSecurityConfig(XmlResourceParser parser) Loading @@ -263,9 +274,9 @@ public class XmlConfigSource implements ConfigSource { throw new ParserException(parser, "Only one base-config allowed"); } seenBaseConfig = true; baseConfigBuilder = parseConfigEntry(parser, seenDomains, true).first; baseConfigBuilder = parseConfigEntry(parser, seenDomains, null, true).get(0).first; } else if ("domain-config".equals(parser.getName())) { builders.add(parseConfigEntry(parser, seenDomains, false)); builders.addAll(parseConfigEntry(parser, seenDomains, baseConfigBuilder, false)); } else { XmlUtils.skipCurrentTag(parser); } Loading @@ -286,8 +297,15 @@ public class XmlConfigSource implements ConfigSource { for (Pair<NetworkSecurityConfig.Builder, Set<Domain>> entry : builders) { NetworkSecurityConfig.Builder builder = entry.first; Set<Domain> domains = entry.second; // Use the base-config for inheriting any unset values in the domain-config entry. // Set the parent of configs that do not have a parent to the base-config. This can // happen if the base-config comes after a domain-config in the file. // Note that this is safe with regards to children because of the order that // parseConfigEntry returns builders, the parent is always before the children. The // children builders will not have build called until _after_ their parents have their // parent set so everything is consistent. if (builder.getParent() == null) { builder.setParent(baseConfigBuilder); } NetworkSecurityConfig config = builder.build(); for (Domain domain : domains) { configs.add(new Pair<>(domain, config)); Loading
tests/NetworkSecurityConfigTest/res/xml/nested_domains.xml 0 → 100644 +18 −0 Original line number Diff line number Diff line <?xml version="1.0" encoding="utf-8"?> <network-security-config> <domain-config> <domain includeSubdomains="true">android.com</domain> <trust-anchors> <certificates src="system" /> </trust-anchors> <!-- nested config that adds pins --> <domain-config> <domain>developer.android.com</domain> <pin-set> <pin digest="SHA-256">7HIpactkIAq2Y49orFOOQKurWxmmSFZhBCoQYcRhJ3Y=</pin> </pin-set> </domain-config> </domain-config> <base-config cleartextTrafficPermitted="false"> </base-config> </network-security-config>
tests/NetworkSecurityConfigTest/res/xml/nested_domains_override.xml 0 → 100644 +12 −0 Original line number Diff line number Diff line <?xml version="1.0" encoding="utf-8"?> <network-security-config> <base-config cleartextTrafficPermitted="false"> </base-config> <!-- Nested config that overrides parent --> <domain-config cleartextTrafficPermitted="true"> <domain includeSubdomains="true">android.com</domain> <domain-config cleartextTrafficPermitted="false"> <domain>developer.android.com</domain> </domain-config> </domain-config> </network-security-config>
tests/NetworkSecurityConfigTest/src/android/security/net/config/XmlConfigTests.java +29 −0 Original line number Diff line number Diff line Loading @@ -245,6 +245,35 @@ public class XmlConfigTests extends AndroidTestCase { TestUtils.assertUrlConnectionSucceeds(context, "android.com", 443); } public void testNestedDomainConfigs() throws Exception { XmlConfigSource source = new XmlConfigSource(getContext(), R.xml.nested_domains); ApplicationConfig appConfig = new ApplicationConfig(source); assertTrue(appConfig.hasPerDomainConfigs()); NetworkSecurityConfig parent = appConfig.getConfigForHostname("android.com"); NetworkSecurityConfig child = appConfig.getConfigForHostname("developer.android.com"); MoreAsserts.assertNotEqual(parent, child); MoreAsserts.assertEmpty(parent.getPins().pins); MoreAsserts.assertNotEmpty(child.getPins().pins); // Check that the child inherited the cleartext value and anchors. assertFalse(child.isCleartextTrafficPermitted()); MoreAsserts.assertNotEmpty(child.getTrustAnchors()); // Test connections. SSLContext context = TestUtils.getSSLContext(source); TestUtils.assertConnectionSucceeds(context, "android.com", 443); TestUtils.assertConnectionSucceeds(context, "developer.android.com", 443); } public void testNestedDomainConfigsOverride() throws Exception { XmlConfigSource source = new XmlConfigSource(getContext(), R.xml.nested_domains_override); ApplicationConfig appConfig = new ApplicationConfig(source); assertTrue(appConfig.hasPerDomainConfigs()); NetworkSecurityConfig parent = appConfig.getConfigForHostname("android.com"); NetworkSecurityConfig child = appConfig.getConfigForHostname("developer.android.com"); MoreAsserts.assertNotEqual(parent, child); assertTrue(parent.isCleartextTrafficPermitted()); assertFalse(child.isCleartextTrafficPermitted()); } private void testBadConfig(int configId) throws Exception { try { XmlConfigSource source = new XmlConfigSource(getContext(), configId); Loading