diff options
author | Jiri Vanek <[email protected]> | 2013-04-17 14:22:01 +0200 |
---|---|---|
committer | Jiri Vanek <[email protected]> | 2013-04-17 14:22:01 +0200 |
commit | 72ac500dc654bbc82332712022cca573db0bc3e6 (patch) | |
tree | 2c6e9d8702ba39d30f0daad5d00dd9edbfcb5a10 /netx | |
parent | 8b462c07f200a80028ffc59027a291837fcf2f1b (diff) |
Fixed gifar vulnereability with automated testcase
Diffstat (limited to 'netx')
10 files changed, 238 insertions, 12 deletions
diff --git a/netx/net/sourceforge/jnlp/Launcher.java b/netx/net/sourceforge/jnlp/Launcher.java index c6712cd..11ca09f 100644 --- a/netx/net/sourceforge/jnlp/Launcher.java +++ b/netx/net/sourceforge/jnlp/Launcher.java @@ -30,7 +30,7 @@ import java.util.Arrays; import java.util.LinkedList; import java.util.List; import java.util.Map; -import java.util.jar.JarFile; +import net.sourceforge.jnlp.util.JarFile; import net.sourceforge.jnlp.cache.CacheUtil; import net.sourceforge.jnlp.cache.UpdatePolicy; diff --git a/netx/net/sourceforge/jnlp/resources/Messages.properties b/netx/net/sourceforge/jnlp/resources/Messages.properties index b372336..792bc64 100644 --- a/netx/net/sourceforge/jnlp/resources/Messages.properties +++ b/netx/net/sourceforge/jnlp/resources/Messages.properties @@ -191,6 +191,7 @@ BOStrict = Enables strict checking of JNLP file format. BOViewer = Shows the trusted certificate viewer. BXnofork = Do not create another JVM. BXclearcache= Clean the JNLP application cache. +BXignoreheaders= Skip jar header verification. BOHelp = Print this message and exit. # Cache diff --git a/netx/net/sourceforge/jnlp/resources/Messages_cs_CZ.properties b/netx/net/sourceforge/jnlp/resources/Messages_cs_CZ.properties index c6da0a5..e8570ff 100644 --- a/netx/net/sourceforge/jnlp/resources/Messages_cs_CZ.properties +++ b/netx/net/sourceforge/jnlp/resources/Messages_cs_CZ.properties @@ -188,6 +188,7 @@ BOStrict= Zapne striktn\u00ed kontrolu souborov\u00e9ho form\u00e1tu JNLP. BOViewer= Zobraz\u00ed prohl\u00ed\u017ee\u010d d\u016fv\u011bryhodn\u00fdch certifik\u00e1t\u016f. BXnofork= Zak\u00e1\u017ee vytv\u00e1\u0159en\u00ed jin\u00fdch prost\u0159ed\u00ed JVM. BXclearcache= Vy\u010dist\u00ed vyrovn\u00e1vac\u00ed pam\u011b\u0165 aplikace JNLP. +BXignoreheaders= Nebude prov\u00e1d\u011bt verifikaci hlavi\u010dek soubor\u016f jar. BOHelp= Vyp\u00ed\u0161e zadanou zpr\u00e1vu do konzole a ukon\u010d\u00ed aplikaci. # Cache diff --git a/netx/net/sourceforge/jnlp/runtime/Boot.java b/netx/net/sourceforge/jnlp/runtime/Boot.java index cd7d71a..ecedcfa 100644 --- a/netx/net/sourceforge/jnlp/runtime/Boot.java +++ b/netx/net/sourceforge/jnlp/runtime/Boot.java @@ -102,6 +102,7 @@ public final class Boot implements PrivilegedAction<Void> { + " -strict " + R("BOStrict") + "\n" + " -Xnofork " + R("BXnofork") + "\n" + " -Xclearcache " + R("BXclearcache") + "\n" + + " -Xignoreheaders " + R("BXignoreheaders") + "\n" + " -help " + R("BOHelp") + "\n"; private static final String doubleArgs = "-basedir -jnlp -arg -param -property -update"; @@ -159,6 +160,9 @@ public final class Boot implements PrivilegedAction<Void> { if (null != getOption("-Xtrustall")) { JNLPRuntime.setTrustAll(true); } + if (null != getOption("-Xignoreheaders")) { + JNLPRuntime.setIgnoreHeaders(true); + } JNLPRuntime.setInitialArgments(Arrays.asList(argsIn)); diff --git a/netx/net/sourceforge/jnlp/runtime/CachedJarFileCallback.java b/netx/net/sourceforge/jnlp/runtime/CachedJarFileCallback.java index 48364df..7824881 100644 --- a/netx/net/sourceforge/jnlp/runtime/CachedJarFileCallback.java +++ b/netx/net/sourceforge/jnlp/runtime/CachedJarFileCallback.java @@ -49,7 +49,7 @@ import java.security.PrivilegedActionException; import java.security.PrivilegedExceptionAction; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; -import java.util.jar.JarFile; +import net.sourceforge.jnlp.util.JarFile; import net.sourceforge.jnlp.util.UrlUtils; @@ -81,7 +81,7 @@ final class CachedJarFileCallback implements URLJarFileCallBack { } @Override - public JarFile retrieve(URL url) throws IOException { + public java.util.jar.JarFile retrieve(URL url) throws IOException { URL localUrl = mapping.get(url); if (localUrl == null) { @@ -122,8 +122,8 @@ final class CachedJarFileCallback implements URLJarFileCallBack { /* * This method is a copy of URLJarFile.retrieve() without the callback check. */ - private JarFile cacheJarFile(URL url) throws IOException { - JarFile result = null; + private java.util.jar.JarFile cacheJarFile(URL url) throws IOException { + java.util.jar.JarFile result = null; final int BUF_SIZE = 2048; @@ -132,9 +132,9 @@ final class CachedJarFileCallback implements URLJarFileCallBack { try { result = - AccessController.doPrivileged(new PrivilegedExceptionAction<JarFile>() { + AccessController.doPrivileged(new PrivilegedExceptionAction<java.util.jar.JarFile>() { @Override - public JarFile run() throws IOException { + public java.util.jar.JarFile run() throws IOException { OutputStream out = null; File tmpFile = null; try { diff --git a/netx/net/sourceforge/jnlp/runtime/JNLPClassLoader.java b/netx/net/sourceforge/jnlp/runtime/JNLPClassLoader.java index 983979e..e790746 100644 --- a/netx/net/sourceforge/jnlp/runtime/JNLPClassLoader.java +++ b/netx/net/sourceforge/jnlp/runtime/JNLPClassLoader.java @@ -60,7 +60,7 @@ import java.util.TreeSet; import java.util.Vector; import java.util.concurrent.ConcurrentHashMap; import java.util.jar.JarEntry; -import java.util.jar.JarFile; +import net.sourceforge.jnlp.util.JarFile; import java.util.jar.Manifest; import net.sourceforge.jnlp.security.appletextendedsecurity.UnsignedAppletTrustConfirmation; @@ -678,7 +678,7 @@ public class JNLPClassLoader extends URLClassLoader { //to read the cacerts or trusted.certs files. e.printStackTrace(); throw new LaunchException(null, null, R("LSFatal"), - R("LCInit"), R("LFatalVerification"), R("LFatalVerificationInfo")); + R("LCInit"), R("LFatalVerification"), R("LFatalVerificationInfo") + ": " +e.getMessage()); } //Case when at least one jar has some signing diff --git a/netx/net/sourceforge/jnlp/runtime/JNLPRuntime.java b/netx/net/sourceforge/jnlp/runtime/JNLPRuntime.java index add5035..a392d15 100644 --- a/netx/net/sourceforge/jnlp/runtime/JNLPRuntime.java +++ b/netx/net/sourceforge/jnlp/runtime/JNLPRuntime.java @@ -87,7 +87,7 @@ import sun.net.www.protocol.jar.URLJarFile; * @version $Revision: 1.19 $ */ public class JNLPRuntime { - + static { loadResources(); } @@ -142,8 +142,16 @@ public class JNLPRuntime { /** set to false to indicate another JVM should not be spawned, even if necessary */ private static boolean forksAllowed = true; - /** all security dialogs will be consumed and pretented as beeing verified by user and allowed.*/ + /** all security dialogs will be consumed and pretented as being verified by user and allowed.*/ private static boolean trustAll=false; + /** + * Header is not checked and so eg. gifar exploit is possible + * @see http://en.wikipedia.org/wiki/Gifar for this kind of attack. + * However if jar file is a bit corrupted, then it sometimes can work so + * this switch can disable the header check. + * + */ + private static boolean ignoreHeaders=false; /** contains the arguments passed to the jnlp runtime */ private static List<String> initialArguments; @@ -776,4 +784,14 @@ public class JNLPRuntime { return trustAll; } + public static boolean isIgnoreHeaders() { + return ignoreHeaders; + } + + public static void setIgnoreHeaders(boolean ignoreHeaders) { + JNLPRuntime.ignoreHeaders = ignoreHeaders; + } + + + } diff --git a/netx/net/sourceforge/jnlp/tools/JarCertVerifier.java b/netx/net/sourceforge/jnlp/tools/JarCertVerifier.java index 17c2a93..db9699d 100644 --- a/netx/net/sourceforge/jnlp/tools/JarCertVerifier.java +++ b/netx/net/sourceforge/jnlp/tools/JarCertVerifier.java @@ -41,7 +41,7 @@ import java.util.List; import java.util.Map; import java.util.Vector; import java.util.jar.JarEntry; -import java.util.jar.JarFile; +import net.sourceforge.jnlp.util.JarFile; import net.sourceforge.jnlp.JARDesc; import net.sourceforge.jnlp.JNLPFile; diff --git a/netx/net/sourceforge/jnlp/util/InvalidJarHeaderException.java b/netx/net/sourceforge/jnlp/util/InvalidJarHeaderException.java new file mode 100644 index 0000000..66efdda --- /dev/null +++ b/netx/net/sourceforge/jnlp/util/InvalidJarHeaderException.java @@ -0,0 +1,49 @@ +/* +Copyright (C) 2012 Red Hat, Inc. + +This file is part of IcedTea. + +IcedTea is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +IcedTea is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with IcedTea; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ +package net.sourceforge.jnlp.util; + +/** + * Thrown from net.sourceforge.jnlp.utilJArFile when verification of headers fails + * + */ +public class InvalidJarHeaderException extends RuntimeException{ + + public InvalidJarHeaderException(String string) { + super(string); + } + +} diff --git a/netx/net/sourceforge/jnlp/util/JarFile.java b/netx/net/sourceforge/jnlp/util/JarFile.java new file mode 100644 index 0000000..8eee518 --- /dev/null +++ b/netx/net/sourceforge/jnlp/util/JarFile.java @@ -0,0 +1,153 @@ +/* + Copyright (C) 2012 Red Hat, Inc. + + This file is part of IcedTea. + + IcedTea is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + IcedTea is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with IcedTea; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA. + + Linking this library statically or dynamically with other modules is + making a combined work based on this library. Thus, the terms and + conditions of the GNU General Public License cover the whole + combination. + + As a special exception, the copyright holders of this library give you + permission to link this library with independent modules to produce an + executable, regardless of the license terms of these independent + modules, and to copy and distribute the resulting executable under + terms of your choice, provided that you also meet, for each linked + independent module, the terms and conditions of the license of that + module. An independent module is a module which is not derived from + or based on this library. If you modify this library, you may extend + this exception to your version of the library, but you are not + obligated to do so. If you do not wish to do so, delete this + exception statement from your version. */ +package net.sourceforge.jnlp.util; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.zip.ZipFile; +import net.sourceforge.jnlp.runtime.JNLPRuntime; + +public class JarFile extends java.util.jar.JarFile { + + public JarFile(String name) throws IOException { + super(name); + verifyZipHeader(new File(name)); + } + + /** + */ + public JarFile(String name, boolean verify) throws IOException { + super(name, verify); + verifyZipHeader(new File(name)); + } + + /** + */ + public JarFile(File file) throws IOException { + super(file); + verifyZipHeader(file); + } + + /** + */ + public JarFile(File file, boolean verify) throws IOException { + super(file, verify); + verifyZipHeader(file); + } + + /* + */ + public JarFile(File file, boolean verify, int mode) throws IOException { + super(file, verify, mode); + verifyZipHeader(file); + } + + + + + /** + * According to specification - + * http://www.pkware.com/documents/casestudies/APPNOTE.TXT or just google + * around zip header all entries in zip-compressed must start with well + * known "PK" which is defined as hexa x50 x4b x03 x04, which in decimal are + * 80 75 3 4. + * + * Note - this is not file-header, it is item-header. + * + * Actually most of compressing formats have some n-bytes header se eg: + * http://www.gzip.org/zlib/rfc-gzip.html#header-trailer for ID1 and ID2 so + * in case that some differently compressed jars will come to play, this is + * the palce where to fix it. + * + */ + private static final byte[] ZIP_LOCAL_FILE_HEADER_SIGNATURE = new byte[]{80, 75, 3, 4}; + + /** + * This method is checking first four bytes of jar-file against + * ZIP_LOCAL_FILE_HEADER_SIGNATURE + * + * Although zip specification allows to skip all corrupted entries, it is + * not safe for jars. If first four bytes of file are not zip + * ZIP_LOCAL_FILE_HEADER_SIGNATURE then exception is thrown + * + * As noted, ZIP_LOCAL_FILE_HEADER_SIGNATURE is not ile-header, but is item-header. + * Possible attack is using the fact that entries without header are considered + * corrupted and so can be ignoered. However, for other they can have some meaning. + * + * So for our purposes we must insists on first record to be valid. + * + * @param file + * @throws IOException, InvalidJarHeaderException + */ + public static void verifyZipHeader(File file) throws IOException { + if (!JNLPRuntime.isIgnoreHeaders()) { + InputStream s = new FileInputStream(file); + try { + byte[] buffer = new byte[ZIP_LOCAL_FILE_HEADER_SIGNATURE.length]; + /* + * for case that new byte[] will accidently initialize same + * sequence as zip header and during the read the buffer will not be filled + */ + for (int i = 0; i < buffer.length; i++) { + buffer[i] = 0; + } + int toRead = ZIP_LOCAL_FILE_HEADER_SIGNATURE.length; + int readSoFar = 0; + int n = 0; + /* + * this is used instead of s.read(buffer) for case of block and + * so returned not-fully-filled dbuffer + */ + while ((n = s.read(buffer, readSoFar, buffer.length - readSoFar)) != -1) { + readSoFar += n; + if (readSoFar == toRead) { + break; + } + } + for (int i = 0; i < buffer.length; i++) { + if (buffer[i] != ZIP_LOCAL_FILE_HEADER_SIGNATURE[i]) { + throw new InvalidJarHeaderException("Jar " + file.getName() + " do not heave valid header. You can skip this check by -Xignoreheaders"); + } + } + } finally { + s.close(); + } + } + } +} |