javaappletmanifestjava-web-startsecurity-warning

As of Java 7 update 45, one can no longer lookup manifest information without triggering a warning?


Unfortunately, the workaround given by Oracle and others here (Java applet manifest - Allow all Caller-Allowable-Codebase) for getting around the 7 update 45 problem does NOT work if your app needs to access its loaded signed jar manifests. In my case, our app does this so as to log the relevant manifest info.

With my web start app, everything worked fine and dandy with the "Trusted-Library" attribute that needed to be added for 7u21. With 7u45, removing the "Trusted-Library" attribute and adding in all the additional attributes talked about in other workarounds will NOT work -- I will get the same warning that you would get if you were running 7u21 without the Trusted-Library attribute (stating the application contains both signed and unsigned code):

Java 7 Update 45 warning]![enter image description here

I've tried just about every manifest/JNLP permutation possible -- nothing cuts the mustard.

What I've found is that, basically, when we load one of our applets' jar manifests (not the JRE jars), and call hasMoreElements, additional security checks occur which trigger the warning:

   public List<String> getManifests() {
          ...         

          Enumeration<URL> urls = getClass().getClassLoader().getResources("META-INF/MANIFEST.MF");

          while (urls.hasMoreElements()) {
                 .... a bunch of loop stuff
                 // at the end of the loop...
                 System.out.println("Checkpoint SGMLOOP8.");

                 System.out.println("Breaking....");
                 //break; <<<<<<---- if the next jar is one of our signed jars, the next line will trigger the warning. If instead we choose to break, the app works perfectly with no warning.

                 System.out.println("urls.hasMoreElements(): " + (urls.hasMoreElements() ? "true" : "false"));  <<<<<<-------- will evaluate to false if user clicks Block on the warning, otherwise will evaluate to true when our signed jars are next

                 System.out.println("Checkpoint SGMLOOP9."); 
          }
          ...
      }

This is what prints out in the Java console at maximum tracing:

Checkpoint SGMLOOP8.
Breaking....                  <<<<---- console output pauses here until user answers warning
security: resource name "META-INF/MANIFEST.MF" in http://<path_to_jar> : java.lang.SecurityException: trusted loader attempted to load sandboxed resource from http://<path_to_jar>
(message repeats for all our signed jars)
urls.hasMoreElements(): false <<<<---- false because user clicked blocked, otherwise true when user clicks don't block
Checkpoint SGMLOOP9.

It took me FOREVER to figure out this out, because for some reason when a signed manifest that passes security checks earlier in the startup process, and then later is accessed and is complained about, I don't naturally think it's complaining about the manifest, but rather the resources referenced by the manifest. Go figure!

Looking into the Java source code, I can see why the warning could possibly happen (hasMoreElements leads to more security checks):

// getResources is called in my code above
java.lang.ClassLoader
public Enumeration<URL> getResources(String name) throws IOException {
    Enumeration[] tmp = new Enumeration[2];
    if (parent != null) {
        tmp[0] = parent.getResources(name);
    } else {
        tmp[0] = getBootstrapResources(name);
    }
    tmp[1] = findResources(name); <<<<------ This returns a new Enumeration<URL> object which has its own “hasMoreElments()” method overwritten – see below code

    return new CompoundEnumeration<>(tmp);
}

java.net.URLClassLoader
public Enumeration<URL> findResources(final String name)
    throws IOException
{
    final Enumeration<URL> e = ucp.findResources(name, true);

    return new Enumeration<URL>() {
        private URL url = null;

        private boolean next() {
            if (url != null) {
                return true;
            }
            do {
                URL u = AccessController.doPrivileged(  <<-- Security context could block this
                    new PrivilegedAction<URL>() {
                        public URL run() {
                            if (!e.hasMoreElements())
                                return null;
                            return e.nextElement();
                        }
                    }, acc);
                if (u == null)
                    break;
                url = ucp.checkURL(u);  <<-- Security checks done in here
            } while (url == null);
            return url != null;
        }

        public URL nextElement() {
            if (!next()) {
                throw new NoSuchElementException();
            }
            URL u = url;
            url = null;
            return u;
        }

        public boolean hasMoreElements() {
            return next();
        }
    };
}

Yes, the manifests are properly signed! Yes, the manifests do have the appropriate attributes! In fact, this is proven by the fact that the jars load just fine and execute, as long as we don't try to directly access their manifests! Just to assuage your fears though, here are the relevant manifest attributes (I've tried MANY additions/subtractions of the below attributes):

Manifest-Version: 1.0
Ant-Version: Apache Ant 1.7.0
Created-By: 24.45-b08 (Oracle Corporation)
Application-Name: AppName
Codebase: *
Permissions: all-permissions
Application-Library-Allowable-Codebase: *
Caller-Allowable-Codebase: *
Trusted-Only: false
Class-Path: jar1.jar jar2.jar jar3.jar
Specification-Title: AppName
Specification-Version: 1.0
Specification-Vendor: CompanyName
Implementation-Title: AppName
Implementation-Version: 1.0
Implementation-Vendor: CompanyName

The question is: Should the warning be happening when we try to access the manifests? As it stands, we either have to choose to force users see the warning every time, or we have to remove our logging of our signed jar manifest info. Seems like a bad choice, especially since this manifest info is very useful for debugging issues as it is really the only way to verify an end-user is running the correct version of the app (short of direct physical inspection on-site). This is especially true of our applet since the jars are allowed to be cached on client systems (along with the corresponding JavaScript to access the applet), meaning they could very easily be running the wrong jars after upgrades/downgrades, etc. Not having this info in our logs could lead to large headaches in the future.

Any ideas? This is especially frustrating since Oracle intends to fix the Trusted-Library issue anyway, so putting all this investigative work into this may be doing nothing more than wasting my weekend. Grrr....

EDIT: One observation I had was that first jar that ran into the security exception actually had a dependency on a different jar in my app. I thought, "maybe the dependent jar's manifest should be read in first?" So I forced the load-order so that non-dependent jars would be loaded first. End result? I could see the non-dependent jar now threw the security exception first... and there is still a warning.


Solution

  • Wanted to update this question to say that as of Java 7 Update 55, this issue has been rendered moot since it is possible again to put in both "Trusted-Library" and "Caller-Allowable-Codebase" manifest attributes simultaneously. With both of these attributes in the manifest, the warning will not be triggered.