changeset 510:19f5282f53e8

Fixed gifar vulnereability with automated testcase
author Jiri Vanek <jvanek@redhat.com>
date Wed, 10 Apr 2013 17:49:20 +0200
parents 6883b7d0a2fc
children ccc249a27004
files ChangeLog netx/net/sourceforge/jnlp/Launcher.java netx/net/sourceforge/jnlp/resources/Messages.properties netx/net/sourceforge/jnlp/runtime/Boot.java netx/net/sourceforge/jnlp/runtime/CachedJarFileCallback.java netx/net/sourceforge/jnlp/runtime/JNLPClassLoader.java netx/net/sourceforge/jnlp/runtime/JNLPRuntime.java netx/net/sourceforge/jnlp/tools/JarCertVerifier.java netx/net/sourceforge/jnlp/util/InvalidJarHeaderException.java netx/net/sourceforge/jnlp/util/JarFile.java tests/reproducers/custom/GifarCreator/srcs/Makefile tests/reproducers/signed/GifarBase/resources/gifarView_hacked.html tests/reproducers/signed/GifarBase/resources/gifarView_ok.html tests/reproducers/signed/GifarBase/resources/gifar_applet.jnlp tests/reproducers/signed/GifarBase/resources/gifar_application.jnlp tests/reproducers/signed/GifarBase/resources/happyNonAnimated.gif tests/reproducers/signed/GifarBase/srcs/GifarMain.java tests/reproducers/signed/GifarBase/testcases/GifarTestcases.java
diffstat 18 files changed, 974 insertions(+), 22 deletions(-) [+]
line wrap: on
line diff
--- a/ChangeLog	Wed Apr 10 15:37:00 2013 +0200
+++ b/ChangeLog	Wed Apr 10 17:49:20 2013 +0200
@@ -1,3 +1,43 @@
+2013-04-10  Jiri Vanek <jvanek@redhat.com>
+
+	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. 
+	* tests/reproducers/custom/GifarCreator/srcs/Makefile:  makefile to 
+	join gif and jar to create gifar
+	* tests/reproducers/signed/GifarBase/resources/gifarView_hacked.html: 
+	html with hacked gifar
+	* tests/reproducers/signed/GifarBase/resources/gifarView_ok.html: 
+	html with valid gifs and jars
+	* tests/reproducers/signed/GifarBase/resources/gifar_applet.jnlp: 
+	jnlp applet constructed from hacked gifar
+	* tests/reproducers/signed/GifarBase/resources/gifar_application.jnlp: 
+	jnlp application constructed from hacked gifar
+	* tests/reproducers/signed/GifarBase/srcs/GifarMain.java: 
+	Main method of reproducer
+	* tests/reproducers/signed/GifarBase/testcases/GifarTestcases.java: 
+	Testing methods
+	* tests/reproducers/signed/GifarBase/resources/happyNonAnimated.gif:
+	binary file, image, gif, used to create hacked gifars
+
+	
+
 2013-04-10  Jiri Vanek <jvanek@redhat.com>
 
 	Fixed news
--- a/netx/net/sourceforge/jnlp/Launcher.java	Wed Apr 10 15:37:00 2013 +0200
+++ b/netx/net/sourceforge/jnlp/Launcher.java	Wed Apr 10 17:49:20 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;
--- a/netx/net/sourceforge/jnlp/resources/Messages.properties	Wed Apr 10 15:37:00 2013 +0200
+++ b/netx/net/sourceforge/jnlp/resources/Messages.properties	Wed Apr 10 17:49:20 2013 +0200
@@ -94,7 +94,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?
@@ -185,6 +185,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
--- a/netx/net/sourceforge/jnlp/runtime/Boot.java	Wed Apr 10 15:37:00 2013 +0200
+++ b/netx/net/sourceforge/jnlp/runtime/Boot.java	Wed Apr 10 17:49:20 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));
 
--- a/netx/net/sourceforge/jnlp/runtime/CachedJarFileCallback.java	Wed Apr 10 15:37:00 2013 +0200
+++ b/netx/net/sourceforge/jnlp/runtime/CachedJarFileCallback.java	Wed Apr 10 17:49:20 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<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 {
--- a/netx/net/sourceforge/jnlp/runtime/JNLPClassLoader.java	Wed Apr 10 15:37:00 2013 +0200
+++ b/netx/net/sourceforge/jnlp/runtime/JNLPClassLoader.java	Wed Apr 10 17:49:20 2013 +0200
@@ -56,7 +56,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;
@@ -612,7 +612,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
--- a/netx/net/sourceforge/jnlp/runtime/JNLPRuntime.java	Wed Apr 10 15:37:00 2013 +0200
+++ b/netx/net/sourceforge/jnlp/runtime/JNLPRuntime.java	Wed Apr 10 17:49:20 2013 +0200
@@ -87,7 +87,7 @@
  * @version $Revision: 1.19 $
  */
 public class JNLPRuntime {
-
+    
     static {
         loadResources();
     }
@@ -142,8 +142,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<String> initialArguments;
@@ -775,4 +783,14 @@
         return trustAll;
     }
 
+    public static boolean isIgnoreHeaders() {
+        return ignoreHeaders;
+    }
+
+    public static void setIgnoreHeaders(boolean ignoreHeaders) {
+        JNLPRuntime.ignoreHeaders = ignoreHeaders;
+    }
+    
+    
+
 }
--- a/netx/net/sourceforge/jnlp/tools/JarCertVerifier.java	Wed Apr 10 15:37:00 2013 +0200
+++ b/netx/net/sourceforge/jnlp/tools/JarCertVerifier.java	Wed Apr 10 17:49:20 2013 +0200
@@ -27,19 +27,35 @@
 
 import static net.sourceforge.jnlp.runtime.Translator.R;
 
-import java.io.*;
-import java.util.*;
-import java.util.jar.*;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.security.CodeSigner;
+import java.security.KeyStore;
+import java.security.cert.CertPath;
 import java.security.cert.Certificate;
 import java.security.cert.X509Certificate;
-import java.security.cert.CertPath;
-import java.security.*;
-import sun.security.x509.*;
-import sun.security.util.*;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Vector;
+import java.util.jar.JarEntry;
+import net.sourceforge.jnlp.util.JarFile;
 
-import net.sourceforge.jnlp.*;
-import net.sourceforge.jnlp.cache.*;
-import net.sourceforge.jnlp.security.*;
+import net.sourceforge.jnlp.JARDesc;
+import net.sourceforge.jnlp.JNLPFile;
+import net.sourceforge.jnlp.LaunchException;
+import net.sourceforge.jnlp.cache.ResourceTracker;
+import net.sourceforge.jnlp.runtime.JNLPRuntime;
+import net.sourceforge.jnlp.security.CertVerifier;
+import net.sourceforge.jnlp.security.CertificateUtils;
+import net.sourceforge.jnlp.security.KeyStores;
+import sun.security.util.DerInputStream;
+import sun.security.util.DerValue;
+import sun.security.x509.NetscapeCertTypeExtension;
 
 /**
  * <p>The jar certificate verifier utility.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/netx/net/sourceforge/jnlp/util/InvalidJarHeaderException.java	Wed Apr 10 17:49:20 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);
+    }
+    
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/netx/net/sourceforge/jnlp/util/JarFile.java	Wed Apr 10 17:49:20 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();
+            }
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/reproducers/custom/GifarCreator/srcs/Makefile	Wed Apr 10 17:49:20 2013 +0200
@@ -0,0 +1,17 @@
+DEPLOY_SUBDIR=$(REPRODUCERS_TESTS_SERVER_DEPLOYDIR)
+GIF=$(DEPLOY_SUBDIR)/happyNonAnimated.gif
+JAR=$(DEPLOY_SUBDIR)/GifarBase.jar
+RESULT1=$(DEPLOY_SUBDIR)/Gifar.jar
+RESULT2=$(DEPLOY_SUBDIR)/Gifar.gif
+
+#this is dependent on reproducers/signed/GifarBase
+
+prepare-reproducer:
+	cat $(GIF) > $(RESULT1)
+	cat $(JAR) >> $(RESULT1)
+	cp $(RESULT1) $(RESULT2)
+
+clean-reproducer:
+	rm -rf $(RESULT1)
+	rm -rf $(RESULT2)
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/reproducers/signed/GifarBase/resources/gifarView_hacked.html	Wed Apr 10 17:49:20 2013 +0200
@@ -0,0 +1,48 @@
+<!--
+
+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.
+
+ -->
+<html><head></head><body bgcolor="blue">
+<p><applet code="GifarMain.class" archive="Gifar.gif" codebase="." width="250" height="200">
+  <param name="image" value="Gifar.jar">
+</applet></p>
+<p>
+<img alt="There should be gif image" src="happyNonAnimated.gif"/>
+<img alt="There should be gif image" src="Gifar.jar"/>
+<img alt="There should be gif image" src="Gifar.gif"/>
+</p>
+</body>
+</html>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/reproducers/signed/GifarBase/resources/gifarView_ok.html	Wed Apr 10 17:49:20 2013 +0200
@@ -0,0 +1,48 @@
+<!--
+
+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.
+
+ -->
+<html><head></head><body bgcolor="blue">
+<p><applet code="GifarMain.class" archive="GifarBase.jar" codebase="." width="250" height="200">
+  <param name="image" value="Gifar.jar">
+</applet></p>
+<p>
+<img alt="There should be gif image" src="happyNonAnimated.gif"/>
+<img alt="There should be gif image" src="Gifar.jar"/>
+<img alt="There should be gif image" src="Gifar.gif"/>
+</p>
+</body>
+</html>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/reproducers/signed/GifarBase/resources/gifar_applet.jnlp	Wed Apr 10 17:49:20 2013 +0200
@@ -0,0 +1,65 @@
+<!--
+
+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.
+
+ -->
+<?xml version="1.0" encoding="utf-8"?>
+<jnlp spec="1.0" href="gifar_applet.jnlp" codebase=".">
+    <information>
+        <title>GifarView</title>
+        <vendor>IcedTea</vendor>
+        <homepage href="http://icedtea.classpath.org/wiki/IcedTea-Web#Testing_IcedTea-Web"/>
+        <description>Gifar issue</description>
+        <offline/>
+    </information>
+    <resources>
+        <j2se version="1.4+"/>
+        <jar href="Gifar.gif"/>
+    </resources>
+  <security>
+      <all-permissions/>
+  </security> 
+  <applet-desc
+      documentBase="."
+      name="Gifar"
+      main-class="GifarMain"
+      width="250"
+      height="200">
+        <param name="image" value="Gifar.jar"/>
+  </applet-desc>
+</jnlp>
+
+
+</applet-desc>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/reproducers/signed/GifarBase/resources/gifar_application.jnlp	Wed Apr 10 17:49:20 2013 +0200
@@ -0,0 +1,60 @@
+<!--
+
+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.
+
+ -->
+<?xml version="1.0" encoding="utf-8"?>
+<jnlp spec="1.0" href="gifar_application.jnlp" codebase=".">
+    <information>
+        <title>GifarView</title>
+        <vendor>IcedTea</vendor>
+        <homepage href="http://icedtea.classpath.org/wiki/IcedTea-Web#Testing_IcedTea-Web"/>
+        <description>Gifar issue</description>
+        <offline/>
+    </information>
+  <security>
+      <all-permissions/>
+  </security> 
+    <resources>
+        <j2se version="1.4+"/>
+        <jar href="Gifar.gif"/>
+    </resources>
+  <application-desc main-class="GifarMain">
+    <argument>Gifar.jar</argument>
+  </application-desc>
+</jnlp>
+
+
+</applet-desc>
Binary file tests/reproducers/signed/GifarBase/resources/happyNonAnimated.gif has changed
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/reproducers/signed/GifarBase/srcs/GifarMain.java	Wed Apr 10 17:49:20 2013 +0200
@@ -0,0 +1,212 @@
+
+import java.awt.BorderLayout;
+import java.awt.image.BufferedImage;
+import java.net.URL;
+import javax.imageio.ImageIO;
+import javax.jnlp.BasicService;
+import javax.jnlp.ServiceManager;
+import javax.swing.Icon;
+import javax.swing.ImageIcon;
+import javax.swing.JApplet;
+import javax.swing.JFrame;
+import javax.swing.JLabel;
+
+/* AppletTest.java
+ Copyright (C) 2011 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, version 2.
+
+ 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.
+ */
+public class GifarMain extends JApplet {
+
+    boolean isApplet = true;
+    String defaultPath = "happyNonAnimated.gif";
+    String imageName;
+    URL path = null;
+
+    private class Killer extends Thread {
+
+        public int n = 3000;
+
+        @Override
+        public void run() {
+            try {
+                Thread.sleep(n);
+                System.out.println("gifar killing himself after " + n + " ms of life");
+                System.exit(0);
+            } catch (Exception ex) {
+            }
+        }
+    }
+    private Killer killer;
+
+    @Override
+    public void init() {
+        System.out.println("gifar was initialised");
+        killer = new Killer();
+        this.setLayout(new BorderLayout());
+        String futurePath = null;
+        if (isApplet) {
+            futurePath = getParameter("image");
+            if ("yes".equals(futurePath)) {
+                imageName = defaultPath;
+            } else if (futurePath != null) {
+                imageName = futurePath;
+            }
+        }
+    }
+
+    @Override
+    public void start() {
+        System.out.println("gifar is starting");
+        String s = "<html>" + System.getProperty("java.vm.version") + "<br>"
+                + System.getProperty("java.vm.vendor") + "<br>"
+                + System.getProperty("java.vm.name") + "<br>"
+                + System.getProperty("java.specification.version") + "<br>"
+                + System.getProperty("java.specification.vendor") + "<br>"
+                + System.getProperty("java.specification.name") + "</html>";
+        JLabel jLabel1 = new JLabel(s);
+        this.add(jLabel1, BorderLayout.NORTH);
+        System.out.println("Used image: " + imageName);
+        if (imageName != null) {
+            try {
+                path = new URL(getIndependentCodebase(), imageName);
+                System.out.println("Loading: "+path.toString());
+                JLabel jLabel2 = new JLabel(loadIcon(path));
+                System.out.println("Image loaded");
+                this.add(jLabel2, BorderLayout.SOUTH);
+            } catch (Exception ex) {
+                //ex.printStackTrace();
+                throw new RuntimeException(ex);
+            }
+        }
+        killer.start();
+        System.out.println("is applet: " + isApplet);
+        System.out.println("gifar was started");
+    }
+
+    Icon loadIcon(URL u) {
+        try {
+            BufferedImage i = ImageIO.read(u.openStream());
+            return new ImageIcon(i);
+        } catch (Exception ex) {
+            throw new RuntimeException(ex);
+        }
+
+
+    }
+
+    public URL getIndependentCodebase() {
+        try {
+            URL u1 = getCodeBaseApplet();
+            URL u2 = getCodeBaseJavaws();
+            if (u1 != null) {
+                return u1;
+            }
+            if (u2 != null) {
+                return u2;
+            }
+            return new URL("http://localhost:44321/");
+        } catch (Exception ex) {
+            throw new RuntimeException(ex);
+        }
+
+    }
+
+    public URL getCodeBaseApplet() {
+        try {
+            URL codebase = getCodeBase();
+            if (codebase != null) {
+                System.out.println("applet codebase: " + codebase.toString());
+                return codebase;
+            } else {
+                System.out.println("applet codebase: null");
+            }
+        } catch (Exception ex) {
+            ex.printStackTrace();
+            System.out.println("applet codebase: null");
+        }
+        return null;
+    }
+
+    public URL getCodeBaseJavaws() {
+        try {
+            BasicService bs = (BasicService) ServiceManager.lookup("javax.jnlp.BasicService");
+            URL codebase = bs.getCodeBase();
+            if (codebase != null) {
+                System.out.println("javaws codebase: " + codebase.toString());
+                return codebase;
+            } else {
+                System.out.println("javaws codebase: null");
+            }
+        } catch (Exception ex) {
+            ex.printStackTrace();
+            System.out.println("javaws codebase: null");
+        }
+        return null;
+    }
+
+    @Override
+    public void stop() {
+        System.out.println("gifar was stopped");
+    }
+
+    @Override
+    public void destroy() {
+        System.out.println("gifar will be destroyed");
+    }
+
+    public static void main(String args[]) {
+        final JFrame f = new JFrame();
+        f.setLayout(new BorderLayout());
+        f.setSize(250, 200);
+        GifarMain gm = new GifarMain();
+        gm.isApplet = false;
+        f.add(gm);
+        gm.init();
+        if (args.length > 0) {
+            if ("yes".equals(args[0])) {
+                gm.imageName = gm.defaultPath;
+            } else {
+                gm.imageName = args[0];
+            }
+        }
+        gm.start();
+        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
+        java.awt.EventQueue.invokeLater(new Runnable() {
+            public void run() {
+                f.setVisible(true);
+            }
+        });
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/reproducers/signed/GifarBase/testcases/GifarTestcases.java	Wed Apr 10 17:49:20 2013 +0200
@@ -0,0 +1,221 @@
+/* AppletTestTests.java
+ Copyright (C) 2011 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, version 2.
+
+ 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.
+ */
+
+import java.awt.image.BufferedImage;
+import java.io.File;
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.List;
+import javax.imageio.ImageIO;
+import net.sourceforge.jnlp.ProcessResult;
+import net.sourceforge.jnlp.ServerAccess;
+import net.sourceforge.jnlp.annotations.NeedsDisplay;
+import net.sourceforge.jnlp.annotations.TestInBrowsers;
+import net.sourceforge.jnlp.browsertesting.BrowserTest;
+import net.sourceforge.jnlp.browsertesting.Browsers;
+import net.sourceforge.jnlp.runtime.JNLPRuntime;
+import net.sourceforge.jnlp.util.InvalidJarHeaderException;
+import net.sourceforge.jnlp.util.JarFile;
+import org.junit.Assert;
+import org.junit.Test;
+
+public class GifarTestcases extends BrowserTest {
+
+    List<String> trustIgnore = Arrays.asList(new String[]{ServerAccess.HEADLES_OPTION, "-Xtrustall", "-Xignoreheaders"});
+    List<String> trust = Arrays.asList(new String[]{ServerAccess.HEADLES_OPTION, "-Xtrustall"});
+    private static String exceptionString = InvalidJarHeaderException.class.getName();
+    private static String okString = "Image loaded";
+    private static String sucideString = "gifar killing himself";
+
+    private static boolean evaluate(String what, String doContains) {
+        return what.contains(doContains);
+    }
+
+    private static boolean exceptionRule(String what) {
+        return evaluate(what, exceptionString);
+    }
+
+    private static boolean okRule(String what) {
+        return evaluate(what, okString);
+    }
+
+    private static boolean sucideRule(String what) {
+        return evaluate(what, sucideString);
+    }
+
+    private static  String toPassingString(String rule) {
+        return "should contain `" + rule + "`";
+    }
+
+    private static  String toFailingString(String rule) {
+        return "should NOT contain `" + rule + "`";
+    }
+    
+    File okJar = new File(server.getDir(), "GifarBase.jar");
+    File hackedJar = new File(server.getDir(), "Gifar.jar");
+    File okImage = new File(server.getDir(), "happyNonAnimated.gif");
+    File hackedImage = new File(server.getDir(), "Gifar.gif");
+
+    @Test
+    public void unittest_verify_okJar() throws IOException {
+        JNLPRuntime.setIgnoreHeaders(false);
+        JarFile j1 = new JarFile(okJar);
+        Assert.assertNotNull(j1);
+        JNLPRuntime.setIgnoreHeaders(true);
+        JarFile j2 = new JarFile(okJar);
+        Assert.assertNotNull(j2);
+
+    }
+
+    @Test
+    public void unittest_verify_badJar() throws IOException {
+        JNLPRuntime.setIgnoreHeaders(false);
+        Exception ex = null;
+        JarFile j1 = null;
+        try {
+            j1 = new JarFile(hackedJar);
+        } catch (InvalidJarHeaderException e) {
+            ex = e;
+        }
+        Assert.assertNull(j1);
+        Assert.assertNotNull(ex);
+        Assert.assertEquals(InvalidJarHeaderException.class, ex.getClass());
+        JNLPRuntime.setIgnoreHeaders(true);
+        JarFile j2 = new JarFile(hackedJar);
+        Assert.assertNotNull(j2);
+
+    }
+
+    @Test
+    public void unittest_verify_badImageAsJar() throws IOException {
+        JNLPRuntime.setIgnoreHeaders(false);
+        Exception ex = null;
+        JarFile j1 = null;
+        try {
+            j1 = new JarFile(hackedImage);
+        } catch (InvalidJarHeaderException e) {
+            ex = e;
+        }
+        Assert.assertNull(j1);
+        Assert.assertNotNull(ex);
+        Assert.assertEquals(InvalidJarHeaderException.class, ex.getClass());
+        JNLPRuntime.setIgnoreHeaders(true);
+        JarFile j2 = new JarFile(hackedImage);
+        Assert.assertNotNull(j2);
+
+    }
+
+    @Test
+    public void unittest_verify_okImage() throws IOException {
+        JNLPRuntime.setIgnoreHeaders(false);
+        BufferedImage j1 = ImageIO.read(okImage);
+        Assert.assertNotNull(j1);
+        JNLPRuntime.setIgnoreHeaders(true);
+        BufferedImage j2 = ImageIO.read(okImage);
+        Assert.assertNotNull(j2);
+
+    }
+
+    @Test
+    public void unittest_verify_badImaqe() throws IOException {
+        JNLPRuntime.setIgnoreHeaders(false);
+        BufferedImage j1 = ImageIO.read(hackedImage);
+        Assert.assertNotNull(j1);
+        JNLPRuntime.setIgnoreHeaders(true);
+        BufferedImage j2 = ImageIO.read(hackedImage);
+        Assert.assertNotNull(j2);
+
+    }
+
+    @Test
+    @NeedsDisplay
+    public void GifarViaJnlp_application() throws Exception {
+        ProcessResult pr = server.executeJavaws(trust, "gifar_application.jnlp");
+        Assert.assertEquals((Integer) 0, pr.returnValue);
+        Assert.assertFalse("stdout " + toFailingString(okString) + " but did", okRule(pr.stdout));
+        Assert.assertTrue("stderr " + toPassingString(exceptionString) + " but did'nt", exceptionRule(pr.stderr));
+    }
+
+    @Test
+    @NeedsDisplay
+    public void GifarViaJnlp_application_ignoreHeaders() throws Exception {
+        ProcessResult pr = server.executeJavaws(trustIgnore, "gifar_application.jnlp");
+        Assert.assertEquals((Integer) 0, pr.returnValue);
+        Assert.assertTrue("stdout " + toPassingString(okString) + " but didn't", okRule(pr.stdout));
+        Assert.assertFalse("stderr " + toFailingString(exceptionString) + " but did", exceptionRule(pr.stderr));
+    }
+
+    @Test
+    @NeedsDisplay
+    public void GifarViaJnlp_applet() throws Exception {
+        ProcessResult pr = server.executeJavaws(trust, "gifar_applet.jnlp");
+        Assert.assertEquals((Integer) 0, pr.returnValue);
+        Assert.assertFalse("stdout " + toFailingString(okString) + " but did", okRule(pr.stdout));
+        Assert.assertTrue("stderr " + toPassingString(exceptionString) + " but didn't", exceptionRule(pr.stderr));
+    }
+
+    @Test
+    @NeedsDisplay
+    public void GifarViaJnlp_applet_ignoreHeaders() throws Exception {
+        ProcessResult pr = server.executeJavaws(trustIgnore, "gifar_applet.jnlp");
+        Assert.assertEquals((Integer) 0, pr.returnValue);
+        Assert.assertTrue("stdout " + toPassingString(okString) + " but didn't", okRule(pr.stdout));
+        Assert.assertFalse("stderr " + toFailingString(exceptionString) + " but did", exceptionRule(pr.stderr));
+    }
+
+    @Test
+    @TestInBrowsers(testIn = {Browsers.all})
+    @NeedsDisplay
+    public void GifarViaBrowser_hacked() throws Exception {
+        ProcessResult pr = server.executeBrowser("gifarView_hacked.html");
+        Assert.assertFalse("stdout " + toFailingString(okString) + " but did", okRule(pr.stdout));
+        Assert.assertTrue("stderr " + toPassingString(exceptionString) + " but didn't", exceptionRule(pr.stderr));
+
+
+    }
+
+    @Test
+    @TestInBrowsers(testIn = {Browsers.one})
+    @NeedsDisplay
+    public void GifarViaBrowser_ok() throws Exception {
+        ProcessResult pr = server.executeBrowser("gifarView_ok.html");
+        Assert.assertTrue("stdout " + toPassingString(okString) + " but didn't", okRule(pr.stdout));
+        Assert.assertFalse("stderr " + toFailingString(exceptionString) + " but did", exceptionRule(pr.stderr));
+
+
+    }
+}