Skip to content

Commit 8fab4bf

Browse files
erev0siBotPeaches
andauthored
added option to include generic/permissive network security config file durin… (#2791)
* added option to include permissive network security config file during build * added tests for app with existing network config and for app without * minor fixes for pull 2791 * refactor: slim down test app for network config * style: remove extra newlines * refactor: moved network tests to aapt2 * refactor: remove unused exceptions * test (aapt2): ensure aapt2 is used for net-sec-conf * fix (cli): block use of net-sec-conf on aapt1 * fix conflict Co-authored-by: Connor Tumbleson <connor@sourcetoad.com> Co-authored-by: Connor Tumbleson <connor.tumbleson@gmail.com>
1 parent d38ecee commit 8fab4bf

File tree

17 files changed

+414
-5
lines changed

17 files changed

+414
-5
lines changed

brut.apktool/apktool-cli/src/main/java/brut/apktool/Main.java

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -216,6 +216,9 @@ private static void cmdBuild(CommandLine cli) {
216216
if (cli.hasOption("d") || cli.hasOption("debug")) {
217217
buildOptions.debugMode = true;
218218
}
219+
if (cli.hasOption("n") || cli.hasOption("net-sec-conf")) {
220+
buildOptions.netSecConf = true;
221+
}
219222
if (cli.hasOption("v") || cli.hasOption("verbose")) {
220223
buildOptions.verbose = true;
221224
}
@@ -247,6 +250,11 @@ private static void cmdBuild(CommandLine cli) {
247250
outFile = null;
248251
}
249252

253+
if (buildOptions.netSecConf && !buildOptions.useAapt2) {
254+
System.err.println("-n / --net-sec-conf is only supported with --use-aapt2.");
255+
System.exit(1);
256+
}
257+
250258
// try and build apk
251259
try {
252260
if (cli.hasOption("a") || cli.hasOption("aapt")) {
@@ -366,6 +374,11 @@ private static void _Options() {
366374
.desc("Sets android:debuggable to \"true\" in the APK's compiled manifest")
367375
.build();
368376

377+
Option netSecConfOption = Option.builder("n")
378+
.longOpt("net-sec-conf")
379+
.desc("Adds a generic Network Security Configuration file in the output APK")
380+
.build();
381+
369382
Option noDbgOption = Option.builder("b")
370383
.longOpt("no-debug-info")
371384
.desc("don't write out debug info (.local, .param, .line, etc.)")
@@ -473,6 +486,7 @@ private static void _Options() {
473486

474487
buildOptions.addOption(apiLevelOption);
475488
buildOptions.addOption(debugBuiOption);
489+
buildOptions.addOption(netSecConfOption);
476490
buildOptions.addOption(aaptOption);
477491
buildOptions.addOption(originalOption);
478492
buildOptions.addOption(aapt2Option);
@@ -528,6 +542,7 @@ private static void _Options() {
528542
allOptions.addOption(noAssetOption);
529543
allOptions.addOption(keepResOption);
530544
allOptions.addOption(debugBuiOption);
545+
allOptions.addOption(netSecConfOption);
531546
allOptions.addOption(aaptOption);
532547
allOptions.addOption(originalOption);
533548
allOptions.addOption(verboseOption);

brut.apktool/apktool-lib/src/main/java/brut/androlib/Androlib.java

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import brut.androlib.meta.UsesFramework;
2121
import brut.androlib.options.BuildOptions;
2222
import brut.androlib.res.AndrolibResources;
23+
import brut.androlib.res.data.ResConfigFlags;
2324
import brut.androlib.res.data.ResPackage;
2425
import brut.androlib.res.data.ResTable;
2526
import brut.androlib.res.data.ResUnknownFiles;
@@ -35,7 +36,10 @@
3536
import org.apache.commons.io.FileUtils;
3637
import org.apache.commons.io.FilenameUtils;
3738
import org.jf.dexlib2.iface.DexFile;
39+
import org.xml.sax.SAXException;
3840

41+
import javax.xml.parsers.ParserConfigurationException;
42+
import javax.xml.transform.TransformerException;
3943
import java.io.*;
4044
import java.util.*;
4145
import java.util.logging.Logger;
@@ -486,6 +490,23 @@ public boolean buildResourcesFull(File appDir, UsesFramework usesFramework)
486490
}
487491
}
488492

493+
if (buildOptions.netSecConf) {
494+
MetaInfo meta = readMetaFile(new ExtFile(appDir));
495+
if (meta.sdkInfo != null && meta.sdkInfo.get("targetSdkVersion") != null) {
496+
if (Integer.parseInt(meta.sdkInfo.get("targetSdkVersion")) < ResConfigFlags.SDK_NOUGAT) {
497+
LOGGER.warning("Target SDK version is lower than 24! Network Security Configuration might be ignored!");
498+
}
499+
}
500+
File netSecConfOrig = new File(appDir, "res/xml/network_security_config.xml");
501+
if (netSecConfOrig.exists()) {
502+
LOGGER.info("Replacing existing network_security_config.xml!");
503+
netSecConfOrig.delete();
504+
}
505+
ResXmlPatcher.modNetworkSecurityConfig(netSecConfOrig);
506+
ResXmlPatcher.setNetworkSecurityConfig(new File(appDir, "AndroidManifest.xml"));
507+
LOGGER.info("Added permissive network security config in manifest");
508+
}
509+
489510
File apkFile = File.createTempFile("APKTOOL", null);
490511
apkFile.delete();
491512
resourceFile.delete();
@@ -518,7 +539,7 @@ public boolean buildResourcesFull(File appDir, UsesFramework usesFramework)
518539
apkFile.delete();
519540
}
520541
return true;
521-
} catch (IOException | BrutException ex) {
542+
} catch (IOException | BrutException | ParserConfigurationException | TransformerException | SAXException ex) {
522543
throw new AndrolibException(ex);
523544
}
524545
}

brut.apktool/apktool-lib/src/main/java/brut/androlib/options/BuildOptions.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ public class BuildOptions {
2222
public boolean forceBuildAll = false;
2323
public boolean forceDeleteFramework = false;
2424
public boolean debugMode = false;
25+
public boolean netSecConf = false;
2526
public boolean verbose = false;
2627
public boolean copyOriginalFiles = false;
2728
public final boolean updateFiles = false;

brut.apktool/apktool-lib/src/main/java/brut/androlib/res/xml/ResXmlPatcher.java

Lines changed: 66 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,7 @@
1717
package brut.androlib.res.xml;
1818

1919
import brut.androlib.AndrolibException;
20-
import org.w3c.dom.Document;
21-
import org.w3c.dom.NamedNodeMap;
22-
import org.w3c.dom.Node;
23-
import org.w3c.dom.NodeList;
20+
import org.w3c.dom.*;
2421
import org.xml.sax.SAXException;
2522

2623
import javax.xml.parsers.DocumentBuilder;
@@ -97,6 +94,71 @@ public static void setApplicationDebugTagTrue(File file) {
9794
}
9895
}
9996

97+
/**
98+
* Sets the value of the network security config in the AndroidManifest file
99+
*
100+
* @param file AndroidManifest file
101+
*/
102+
public static void setNetworkSecurityConfig(File file) {
103+
if (file.exists()) {
104+
try {
105+
Document doc = loadDocument(file);
106+
Node application = doc.getElementsByTagName("application").item(0);
107+
108+
// load attr
109+
NamedNodeMap attr = application.getAttributes();
110+
Node netSecConfAttr = attr.getNamedItem("android:networkSecurityConfig");
111+
112+
if (netSecConfAttr == null) {
113+
// there is not an already existing network security configuration
114+
netSecConfAttr = doc.createAttribute("android:networkSecurityConfig");
115+
attr.setNamedItem(netSecConfAttr);
116+
}
117+
118+
// whether it already existed or it was created now set it to the proper value
119+
netSecConfAttr.setNodeValue("@xml/network_security_config");
120+
121+
saveDocument(file, doc);
122+
123+
} catch (SAXException | ParserConfigurationException | IOException | TransformerException ignored) {
124+
}
125+
}
126+
}
127+
128+
/**
129+
* Creates a modified network security config file that is more permissive
130+
*
131+
* @param file network security config file
132+
*/
133+
public static void modNetworkSecurityConfig(File file)
134+
throws ParserConfigurationException, TransformerException, IOException, SAXException {
135+
136+
DocumentBuilderFactory documentFactory = DocumentBuilderFactory.newInstance();
137+
DocumentBuilder documentBuilder = documentFactory.newDocumentBuilder();
138+
Document document = documentBuilder.newDocument();
139+
140+
Element root = document.createElement("network-security-config");
141+
document.appendChild(root);
142+
Element baseConfig = document.createElement("base-config");
143+
root.appendChild(baseConfig);
144+
Element trustAnchors = document.createElement("trust-anchors");
145+
baseConfig.appendChild(trustAnchors);
146+
147+
Element certSystem = document.createElement("certificates");
148+
Attr attrSystem = document.createAttribute("src");
149+
attrSystem.setValue("system");
150+
certSystem.setAttributeNode(attrSystem);
151+
trustAnchors.appendChild(certSystem);
152+
153+
Element certUser = document.createElement("certificates");
154+
Attr attrUser = document.createAttribute("src");
155+
attrUser.setValue("user");
156+
certUser.setAttributeNode(attrUser);
157+
trustAnchors.appendChild(certUser);
158+
159+
saveDocument(file, document);
160+
}
161+
100162
/**
101163
* Any @string reference in a provider value in AndroidManifest.xml will break on
102164
* build, thus preventing the application from installing. This is from a bug/error
Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
/*
2+
* Copyright (C) 2010 Ryszard Wiśniewski <brut.alll@gmail.com>
3+
* Copyright (C) 2010 Connor Tumbleson <connor.tumbleson@gmail.com>
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* https://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
package brut.androlib.aapt2;
18+
19+
import brut.androlib.*;
20+
import brut.androlib.options.BuildOptions;
21+
import brut.common.BrutException;
22+
import brut.directory.ExtFile;
23+
import brut.util.OS;
24+
import org.custommonkey.xmlunit.XMLUnit;
25+
import org.junit.AfterClass;
26+
import org.junit.BeforeClass;
27+
import org.junit.Test;
28+
import org.w3c.dom.Document;
29+
import org.w3c.dom.NamedNodeMap;
30+
import org.w3c.dom.Node;
31+
import org.xml.sax.SAXException;
32+
33+
import javax.xml.parsers.DocumentBuilder;
34+
import javax.xml.parsers.DocumentBuilderFactory;
35+
import javax.xml.parsers.ParserConfigurationException;
36+
import java.io.*;
37+
import java.nio.file.Files;
38+
import java.nio.file.Paths;
39+
40+
import static org.custommonkey.xmlunit.XMLAssert.assertXMLEqual;
41+
import static org.junit.Assert.*;
42+
43+
public class NetworkConfigTest extends BaseTest {
44+
45+
@BeforeClass
46+
public static void beforeClass() throws Exception {
47+
TestUtils.cleanFrameworkFile();
48+
49+
sTmpDir = new ExtFile(OS.createTempDirectory());
50+
sTestOrigDir = new ExtFile(sTmpDir, "testapp-orig");
51+
sTestNewDir = new ExtFile(sTmpDir, "testapp-new");
52+
LOGGER.info("Unpacking testapp...");
53+
TestUtils.copyResourceDir(NetworkConfigTest.class, "aapt2/network_config/", sTestOrigDir);
54+
55+
LOGGER.info("Building testapp.apk...");
56+
BuildOptions buildOptions = new BuildOptions();
57+
buildOptions.netSecConf = true;
58+
buildOptions.useAapt2 = true;
59+
File testApk = new File(sTmpDir, "testapp.apk");
60+
new Androlib(buildOptions).build(sTestOrigDir, testApk);
61+
62+
LOGGER.info("Decoding testapp.apk...");
63+
ApkDecoder apkDecoder = new ApkDecoder(testApk);
64+
apkDecoder.setOutDir(sTestNewDir);
65+
apkDecoder.decode();
66+
}
67+
68+
@AfterClass
69+
public static void afterClass() throws BrutException {
70+
OS.rmdir(sTmpDir);
71+
}
72+
73+
@Test
74+
public void buildAndDecodeTest() {
75+
assertTrue(sTestNewDir.isDirectory());
76+
}
77+
78+
@Test
79+
public void netSecConfGeneric() throws IOException, SAXException {
80+
LOGGER.info("Comparing network security configuration file...");
81+
String expected = TestUtils.replaceNewlines("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>" +
82+
"<network-security-config><base-config><trust-anchors><certificates src=\"system\"/><certificates src=\"us" +
83+
"er\"/></trust-anchors></base-config></network-security-config>");
84+
85+
byte[] encoded = Files.readAllBytes(Paths.get(String.valueOf(sTestNewDir), "res/xml/network_security_config.xml"));
86+
String obtained = TestUtils.replaceNewlines(new String(encoded));
87+
88+
XMLUnit.setIgnoreWhitespace(true);
89+
XMLUnit.setIgnoreAttributeOrder(true);
90+
XMLUnit.setCompareUnmatched(false);
91+
92+
assertXMLEqual(expected, obtained);
93+
}
94+
95+
@Test
96+
public void netSecConfInManifest() throws IOException, ParserConfigurationException, SAXException {
97+
LOGGER.info("Validating network security config in Manifest...");
98+
Document doc = loadDocument(new File(sTestNewDir + "/AndroidManifest.xml"));
99+
Node application = doc.getElementsByTagName("application").item(0);
100+
NamedNodeMap attr = application.getAttributes();
101+
Node debugAttr = attr.getNamedItem("android:networkSecurityConfig");
102+
assertEquals("@xml/network_security_config", debugAttr.getNodeValue());
103+
}
104+
105+
private static Document loadDocument(File file)
106+
throws IOException, SAXException, ParserConfigurationException {
107+
108+
DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance();
109+
docFactory.setFeature(FEATURE_DISABLE_DOCTYPE_DECL, true);
110+
docFactory.setFeature(FEATURE_LOAD_DTD, false);
111+
112+
try {
113+
docFactory.setAttribute(ACCESS_EXTERNAL_DTD, " ");
114+
docFactory.setAttribute(ACCESS_EXTERNAL_SCHEMA, " ");
115+
} catch (IllegalArgumentException ex) {
116+
LOGGER.warning("JAXP 1.5 Support is required to validate XML");
117+
}
118+
119+
DocumentBuilder docBuilder = docFactory.newDocumentBuilder();
120+
try (FileInputStream inputStream = new FileInputStream(file)) {
121+
return docBuilder.parse(inputStream);
122+
}
123+
}
124+
125+
private static final String ACCESS_EXTERNAL_DTD = "http://javax.xml.XMLConstants/property/accessExternalDTD";
126+
private static final String ACCESS_EXTERNAL_SCHEMA = "http://javax.xml.XMLConstants/property/accessExternalSchema";
127+
private static final String FEATURE_LOAD_DTD = "http://apache.org/xml/features/nonvalidating/load-external-dtd";
128+
private static final String FEATURE_DISABLE_DOCTYPE_DECL = "http://apache.org/xml/features/disallow-doctype-decl";
129+
}

0 commit comments

Comments
 (0)