# HG changeset patch # User Jiri Vanek # Date 1365611325 -7200 # Node ID cb58b31c450e531f32dfeedfec602779b33a5a86 # Parent 94234beee2e9fc826ad81e2aa17a56960cc7f8d4 Fixed gifar vulnereability diff -r 94234beee2e9 -r cb58b31c450e ChangeLog --- a/ChangeLog Wed Apr 10 15:39:04 2013 +0200 +++ b/ChangeLog Wed Apr 10 18:28:45 2013 +0200 @@ -1,3 +1,25 @@ +2013-04-10 Jiri Vanek + + Fixed gifar vulnereability with automated testcase + * netx/net/sourceforge/jnlp/util/JarFile.java: IcedTea-Web replacement for + java.util.jar.JarFile.java with capability to verify if the jar starts as jar + and not as something else (eg gif) + * netx/net/sourceforge/jnlp/Launcher.java: migrated to new JarFile + * netx/net/sourceforge/jnlp/resources/Messages.properties: added + BXignoreheaders key with description to new -Xignoreheaders switch + * netx/net/sourceforge/jnlp/runtime/Boot.java: added switch Xignoreheaders + to allow to disable the header verification. + * netx/net/sourceforge/jnlp/runtime/CachedJarFileCallback.java: + migrated to new JarFile + * netx/net/sourceforge/jnlp/runtime/JNLPClassLoader.java: improved + reporting of new JarFile exceptions + * netx/net/sourceforge/jnlp/runtime/JNLPRuntime.java: new field + ignoreHeaders, informing about new JarFile whether to verify or not verify + headers. By default verifying, so have value of false. + * netx/net/sourceforge/jnlp/tools/JarCertVerifier.java: migrated to new JarFile + * netx/net/sourceforge/jnlp/util/InvalidJarHeaderException.java: new + not-checked exception to signify that jar is corrupted on headers level. + 2013-04-10 Jiri Vanek Fixed news diff -r 94234beee2e9 -r cb58b31c450e NEWS --- a/NEWS Wed Apr 10 15:39:04 2013 +0200 +++ b/NEWS Wed Apr 10 18:28:45 2013 +0200 @@ -9,6 +9,8 @@ CVE-XXXX-YYYY: http://www.cve.mitre.org/cgi-bin/cvename.cgi?name=XXXX-YYYY New in release 1.2.3 (2013-04-17): +* Security Updates + - RH884705 - fixed gifar vulnereablity * Common - PR1161: X509VariableTrustManager does not work correctly with OpenJDK7 * Plugin diff -r 94234beee2e9 -r cb58b31c450e netx/net/sourceforge/jnlp/Launcher.java --- a/netx/net/sourceforge/jnlp/Launcher.java Wed Apr 10 15:39:04 2013 +0200 +++ b/netx/net/sourceforge/jnlp/Launcher.java Wed Apr 10 18:28:45 2013 +0200 @@ -29,7 +29,7 @@ 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 -r 94234beee2e9 -r cb58b31c450e netx/net/sourceforge/jnlp/resources/Messages.properties --- a/netx/net/sourceforge/jnlp/resources/Messages.properties Wed Apr 10 15:39:04 2013 +0200 +++ b/netx/net/sourceforge/jnlp/resources/Messages.properties Wed Apr 10 18:28:45 2013 +0200 @@ -93,7 +93,7 @@ LNotVerified=Jars not verified. LCancelOnUserRequest=Canceled on user request. LFatalVerification=A fatal error occurred while trying to verify jars. -LFatalVerificationInfo= +LFatalVerificationInfo=Description LNotVerifiedDialog=Not all jars could be verified. LAskToContinue=Would you still like to continue running this application? @@ -181,6 +181,7 @@ 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 -r 94234beee2e9 -r cb58b31c450e netx/net/sourceforge/jnlp/runtime/Boot.java --- a/netx/net/sourceforge/jnlp/runtime/Boot.java Wed Apr 10 15:39:04 2013 +0200 +++ b/netx/net/sourceforge/jnlp/runtime/Boot.java Wed Apr 10 18:28:45 2013 +0200 @@ -102,6 +102,7 @@ + " -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 @@ if (null != getOption("-Xtrustall")) { JNLPRuntime.setTrustAll(true); } + if (null != getOption("-Xignoreheaders")) { + JNLPRuntime.setIgnoreHeaders(true); + } JNLPRuntime.setInitialArgments(Arrays.asList(argsIn)); diff -r 94234beee2e9 -r cb58b31c450e netx/net/sourceforge/jnlp/runtime/CachedJarFileCallback.java --- a/netx/net/sourceforge/jnlp/runtime/CachedJarFileCallback.java Wed Apr 10 15:39:04 2013 +0200 +++ b/netx/net/sourceforge/jnlp/runtime/CachedJarFileCallback.java Wed Apr 10 18:28:45 2013 +0200 @@ -49,7 +49,7 @@ 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 @@ } @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 @@ /* * 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 @@ try { result = - AccessController.doPrivileged(new PrivilegedExceptionAction() { + AccessController.doPrivileged(new PrivilegedExceptionAction() { @Override - public JarFile run() throws IOException { + public java.util.jar.JarFile run() throws IOException { OutputStream out = null; File tmpFile = null; try { diff -r 94234beee2e9 -r cb58b31c450e netx/net/sourceforge/jnlp/runtime/JNLPClassLoader.java --- a/netx/net/sourceforge/jnlp/runtime/JNLPClassLoader.java Wed Apr 10 15:39:04 2013 +0200 +++ b/netx/net/sourceforge/jnlp/runtime/JNLPClassLoader.java Wed Apr 10 18:28:45 2013 +0200 @@ -50,7 +50,7 @@ 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.AppletDesc; @@ -483,7 +483,7 @@ //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 -r 94234beee2e9 -r cb58b31c450e netx/net/sourceforge/jnlp/runtime/JNLPRuntime.java --- a/netx/net/sourceforge/jnlp/runtime/JNLPRuntime.java Wed Apr 10 15:39:04 2013 +0200 +++ b/netx/net/sourceforge/jnlp/runtime/JNLPRuntime.java Wed Apr 10 18:28:45 2013 +0200 @@ -88,7 +88,7 @@ * @version $Revision: 1.19 $ */ public class JNLPRuntime { - + static { loadResources(); } @@ -146,8 +146,16 @@ /** 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 initialArguments; @@ -816,4 +824,14 @@ return trustAll; } + public static boolean isIgnoreHeaders() { + return ignoreHeaders; + } + + public static void setIgnoreHeaders(boolean ignoreHeaders) { + JNLPRuntime.ignoreHeaders = ignoreHeaders; + } + + + } diff -r 94234beee2e9 -r cb58b31c450e netx/net/sourceforge/jnlp/util/InvalidJarHeaderException.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/netx/net/sourceforge/jnlp/util/InvalidJarHeaderException.java Wed Apr 10 18:28:45 2013 +0200 @@ -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 -r 94234beee2e9 -r cb58b31c450e netx/net/sourceforge/jnlp/util/JarFile.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/netx/net/sourceforge/jnlp/util/JarFile.java Wed Apr 10 18:28:45 2013 +0200 @@ -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(); + } + } + } +}