Blob Blame History Raw
diff --git a/ChangeLog b/ChangeLog
index 0c63dd98..d8e560e0 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,29 @@
+2019-06-26  Jiri Vanek <jvanek@redhat.com>
+
+	All files, except signaturre files, are now  checked for signatures - CVE-2019-10181
+	* b/netx/net/sourceforge/jnlp/tools/JarCertVerifier.java: (isMetaInfFile) fixed bug, when anything in META-INF was not
+	checked for signature. Now only signature files are skipped
+	* tests/netx/unit/net/sourceforge/jnlp/tools/JarCertVerifierTest.java: added tests for check if file should be skipped from 
+	signature check
+
+2019-06-26  Jiri Vanek <jvanek@redhat.com>
+
+	Nested jar, if by relative path point up, is stored as hashed - CVE-2019-10185
+	* tests/netx/unit/net/sourceforge/jnlp/runtime/jar03_dotdotN1.jar: crafted jar with hacked zip entries to be named like ".."
+	* tests/netx/unit/net/sourceforge/jnlp/runtime/jar_03_dotdot_jarN1.jnlp: jnlp to call jar03_dotdotN1.jar
+	* netx/net/sourceforge/jnlp/cache/CacheUtil.jsava: (hex) made public to be reused in JNLPClassLoader
+	* netx/net/sourceforge/jnlp/runtime/JNLPClassLoader.java: if nested jar contains .. in path, is extracted as hashed
+
+2019-06-26  Jiri Vanek <jvanek@redhat.com>
+
+	Fixed bug when relative path (..) could leak up (even out of cache) - CVE-2019-10182
+	* netx/net/sourceforge/jnlp/cache/CacheUtil.java: if path or query contains .. is saved to cache via its hash 
+	* netx/net/sourceforge/jnlp/util/FileUtils.java: added warning about different behavior on win/linux
+	* tests/netx/unit/net/sourceforge/jnlp/cache/CacheUtilTest.java: added tests for hashing
+	* tests/netx/unit/net/sourceforge/jnlp/runtime/JNLPClassLoaderTest.java:  added test for .. in path. Added test
+	that verifies encoded .. (%2E%2E) do not leak from cahce
+	* tests/netx/unit/net/sourceforge/jnlp/runtime/up.jnlp: example jnlp with .. full url
+
 2018-05-14  Jiri Vanek <jvanek@redhat.com>
 
 	* netx/net/sourceforge/jnlp/runtime/AppletEnvironment.java: getDocumentBase now returns codeBase as fallback when 
diff --git a/tests/netx/unit/net/sourceforge/jnlp/cache/CacheUtilTest.java b/tests/netx/unit/net/sourceforge/jnlp/cache/CacheUtilTest.java
index 6b0cd256..5dbf2d69 100644
--- a/tests/netx/unit/net/sourceforge/jnlp/cache/CacheUtilTest.java
+++ b/tests/netx/unit/net/sourceforge/jnlp/cache/CacheUtilTest.java
@@ -135,6 +135,14 @@ public class CacheUtilTest {
         File r = CacheUtil.urlToPath(u, "/tmp/");
         Assert.assertEquals(expected, r);
     }
+
+    @Test
+    public void testQueryGotHAshedToo() throws Exception {
+        final URL u = new URL("https://example2.com/something/my.jar?../../harm");
+        final File expected = new File("/tmp/https/example2.com/2844b3c690ea355159ed61de6e727f2e9169ab55bf58b8fa3f4b64f6a25bd7.jar");
+        File r = CacheUtil.urlToPath(u, "/tmp/");
+        Assert.assertEquals(expected, r);
+    }
     
     
     @Test
diff --git a/tests/netx/unit/net/sourceforge/jnlp/runtime/JNLPClassLoaderTest.java b/tests/netx/unit/net/sourceforge/jnlp/runtime/JNLPClassLoaderTest.java
index 2b28fb93..d86786ab 100644
--- a/tests/netx/unit/net/sourceforge/jnlp/runtime/JNLPClassLoaderTest.java
+++ b/tests/netx/unit/net/sourceforge/jnlp/runtime/JNLPClassLoaderTest.java
@@ -405,6 +405,8 @@ public class JNLPClassLoaderTest extends NoStdOutErrTest {
         JNLPRuntime.setTrustAll(true);
         JNLPRuntime.setSecurityEnabled(false);
         JNLPRuntime.setDebug(true);
+        String manifestAttsBackup = JNLPRuntime.getConfiguration().getProperty(DeploymentConfiguration.KEY_ENABLE_MANIFEST_ATTRIBUTES_CHECK);
+        JNLPRuntime.getConfiguration().setProperty(DeploymentConfiguration.KEY_ENABLE_MANIFEST_ATTRIBUTES_CHECK, "NONE");
         try {
             final JNLPFile jnlpFile1 = new JNLPFile(new URL("http://localhost:" + port + "/up.jnlp"));
             final JNLPClassLoader classLoader1 = JNLPClassLoader.getInstance(jnlpFile1, UpdatePolicy.ALWAYS, false);
@@ -419,6 +421,7 @@ public class JNLPClassLoaderTest extends NoStdOutErrTest {
             JNLPRuntime.setTrustAll(trustBackup);
             JNLPRuntime.setSecurityEnabled(securityBAckup);
             JNLPRuntime.setDebug(verbose);
+            JNLPRuntime.getConfiguration().setProperty(DeploymentConfiguration.KEY_ENABLE_MANIFEST_ATTRIBUTES_CHECK, manifestAttsBackup);
             as.stop();
         }
 
@@ -451,6 +454,11 @@ public class JNLPClassLoaderTest extends NoStdOutErrTest {
         JNLPRuntime.setTrustAll(true);
         JNLPRuntime.setSecurityEnabled(false);
         JNLPRuntime.setDebug(true);
+        //fix of "All files, except signaturre files, are now  checked for signatures" make this actually correctly failing ahead of time
+        String ignoreBackup = JNLPRuntime.getConfiguration().getProperty(DeploymentConfiguration.KEY_SECURITY_ITW_IGNORECERTISSUES);
+        JNLPRuntime.getConfiguration().setProperty(DeploymentConfiguration.KEY_SECURITY_ITW_IGNORECERTISSUES, "true");
+        String manifestAttsBackup = JNLPRuntime.getConfiguration().getProperty(DeploymentConfiguration.KEY_ENABLE_MANIFEST_ATTRIBUTES_CHECK);
+        JNLPRuntime.getConfiguration().setProperty(DeploymentConfiguration.KEY_ENABLE_MANIFEST_ATTRIBUTES_CHECK, "NONE");
         try {
             //it is invalid jar, so we have to disable checks first
             final JNLPFile jnlpFile = new JNLPFile(new URL("http://localhost:" + port + "/jar_03_dotdot_jarN1.jnlp"));
@@ -488,10 +496,102 @@ public class JNLPClassLoaderTest extends NoStdOutErrTest {
             JNLPRuntime.setTrustAll(trustBackup);
             JNLPRuntime.setSecurityEnabled(securityBAckup);
             JNLPRuntime.setDebug(verbose);
+            JNLPRuntime.getConfiguration().setProperty(DeploymentConfiguration.KEY_SECURITY_ITW_IGNORECERTISSUES, ignoreBackup);
+            JNLPRuntime.getConfiguration().setProperty(DeploymentConfiguration.KEY_ENABLE_MANIFEST_ATTRIBUTES_CHECK, manifestAttsBackup);
             as.stop();
         }
 
     }
 
+    @Test(expected = Exception.class)
+    public void testDifferentSignatureInManifestMf() throws Exception {
+        CacheUtil.clearCache();
+        int port = ServerAccess.findFreePort();
+        File dir = FileTestUtils.createTempDirectory();
+        dir.deleteOnExit();
+        File jar = new File(dir,"jar03_dotdotN1.jar");
+        File jnlp = new File(dir,"jar_03_dotdot_jarN1.jnlp");
+        InputStream is1 = this.getClass().getClassLoader().getResourceAsStream("net/sourceforge/jnlp/runtime/jar_03_dotdot_jarN1.jnlp");
+        InputStream is2 = this.getClass().getClassLoader().getResourceAsStream("net/sourceforge/jnlp/runtime/jar03_dotdotN1.jar");
+        OutputStream fos1 = new FileOutputStream(jnlp);
+        OutputStream fos2 = new FileOutputStream(jar);
+        StreamUtils.copyStream(is1, fos1);
+        StreamUtils.copyStream(is2, fos2);
+        fos1.flush();;
+        fos2.flush();
+        fos1.close();
+        fos2.close();
+        ServerLauncher as = ServerAccess.getIndependentInstance(dir.getAbsolutePath(), port);
+        boolean verifyBackup = JNLPRuntime.isVerifying();
+        boolean trustBackup= JNLPRuntime.isTrustAll();
+        boolean securityBAckup= JNLPRuntime.isSecurityEnabled();
+        boolean verbose= JNLPRuntime.isDebug();
+        JNLPRuntime.setVerify(false);
+        JNLPRuntime.setTrustAll(true);
+        JNLPRuntime.setSecurityEnabled(false);
+        JNLPRuntime.setDebug(true);
+        String ignoreBackup = JNLPRuntime.getConfiguration().getProperty(DeploymentConfiguration.KEY_SECURITY_ITW_IGNORECERTISSUES);
+        JNLPRuntime.getConfiguration().setProperty(DeploymentConfiguration.KEY_SECURITY_ITW_IGNORECERTISSUES, "false");
+        try {
+            //it is invalid jar, so we have to disable checks first
+            final JNLPFile jnlpFile = new JNLPFile(new URL("http://localhost:" + port + "/jar_03_dotdot_jarN1.jnlp"));
+            final JNLPClassLoader classLoader = JNLPClassLoader.getInstance(jnlpFile, UpdatePolicy.ALWAYS, false);
+        } finally {
+            JNLPRuntime.setVerify(verifyBackup);
+            JNLPRuntime.setTrustAll(trustBackup);
+            JNLPRuntime.setSecurityEnabled(securityBAckup);
+            JNLPRuntime.setDebug(verbose);
+            JNLPRuntime.getConfiguration().setProperty(DeploymentConfiguration.KEY_SECURITY_ITW_IGNORECERTISSUES, ignoreBackup);
+            as.stop();
+        }
+
+    }
+
+    @Test
+    public void testEncodedPathIsNotDecodedForCache() throws Exception {
+        CacheUtil.clearCache();
+        int port = ServerAccess.findFreePort();
+        File dir = FileTestUtils.createTempDirectory();
+        dir.deleteOnExit();
+        dir = new File(dir,"base");
+        dir.mkdir();
+        File jar = new File(dir,"j1.jar");
+        File jnlp = new File(dir+"/a/b/upEncoded.jnlp");
+        jnlp.getParentFile().mkdirs();
+        InputStream is = this.getClass().getClassLoader().getResourceAsStream("net/sourceforge/jnlp/runtime/upEncoded.jnlp");
+        String jnlpString = StreamUtils.readStreamAsString(is, true, "utf-8");
+        is.close();
+        jnlpString = jnlpString.replaceAll("8080", ""+port);
+        is = this.getClass().getClassLoader().getResourceAsStream("net/sourceforge/jnlp/runtime/j1.jar");
+        StreamUtils.copyStream(is, new FileOutputStream(jar));
+        Files.write(jnlp.toPath(),jnlpString.getBytes("utf-8"));
+        ServerLauncher as = ServerAccess.getIndependentInstance(jnlp.getParent(), port);
+        boolean verifyBackup = JNLPRuntime.isVerifying();
+        boolean trustBackup= JNLPRuntime.isTrustAll();
+        boolean securityBAckup= JNLPRuntime.isSecurityEnabled();
+        boolean verbose= JNLPRuntime.isDebug();
+        JNLPRuntime.setVerify(false);
+        JNLPRuntime.setTrustAll(true);
+        JNLPRuntime.setSecurityEnabled(false);
+        JNLPRuntime.setDebug(true);
+        try {
+            final JNLPFile jnlpFile1 = new JNLPFile(new URL("http://localhost:" + port + "/upEncoded.jnlp"));
+            final JNLPClassLoader classLoader1 = JNLPClassLoader.getInstance(jnlpFile1, UpdatePolicy.ALWAYS, false);
+            InputStream is1 = classLoader1.getResourceAsStream("Hello1.class");
+            is1.close();
+            is1 = classLoader1.getResourceAsStream("META-INF/MANIFEST.MF");
+            is1.close();
+            Assert.assertTrue(new File(PathsAndFiles.CACHE_DIR.getFullPath()+"/0/http/localhost/"+port+"/upEncoded.jnlp").exists());
+            //be aware; if decoding ever come in play here, thios will leak out of cache folder. Thus harm user system. See fix for " Fixed bug when relative path (..) could leak up (even out of cache)"
+            Assert.assertTrue(new File(PathsAndFiles.CACHE_DIR.getFullPath()+"/1/http/localhost/"+port+"/%2E%2E/%2E%2E/%2E%2E/base").exists());
+        } finally {
+            JNLPRuntime.setVerify(verifyBackup);
+            JNLPRuntime.setTrustAll(trustBackup);
+            JNLPRuntime.setSecurityEnabled(securityBAckup);
+            JNLPRuntime.setDebug(verbose);
+            as.stop();
+        }
+
+    }
 
 }
diff --git a/tests/netx/unit/net/sourceforge/jnlp/runtime/upEncoded.jnlp b/tests/netx/unit/net/sourceforge/jnlp/runtime/upEncoded.jnlp
new file mode 100644
index 00000000..f0658bbc
--- /dev/null
+++ b/tests/netx/unit/net/sourceforge/jnlp/runtime/upEncoded.jnlp
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<jnlp spec="6.0+" codebase=".">
+
+  <information><title>1965</title><vendor>Nemzeti Ado- es Vamhivatal</vendor><offline-allowed/></information>
+
+
+  <resources>
+    <j2se href="http://java.sun.com/products/autodl/j2se" version="1.8+" />
+    <!-- absolute url is a must -->
+    <jar href="http://localhost:8080/%2E%2E/%2E%2E/%2E%2E/base/j1.jar" version="2.0"/>
+  </resources>
+
+  <application-desc main-class="Hello1" />
+
+</jnlp>