From 90705ffaa37c62b720865cc8377b2f63f6be92e2 Mon Sep 17 00:00:00 2001 From: Andrew Azores Date: Tue, 3 Dec 2013 11:39:12 -0500 Subject: Implement per-JAR security descriptors (PR1592) Fix/new feature for PR1592. Each JAR in partially signed applets is assigned its own security level, rather than forcing the entire applet to run sandboxed. --- .../sourceforge/jnlp/runtime/JNLPClassLoader.java | 128 +++++++++++++-------- .../sourceforge/jnlp/tools/JarCertVerifier.java | 8 ++ 2 files changed, 88 insertions(+), 48 deletions(-) (limited to 'netx') diff --git a/netx/net/sourceforge/jnlp/runtime/JNLPClassLoader.java b/netx/net/sourceforge/jnlp/runtime/JNLPClassLoader.java index 324bbc6..45be60c 100644 --- a/netx/net/sourceforge/jnlp/runtime/JNLPClassLoader.java +++ b/netx/net/sourceforge/jnlp/runtime/JNLPClassLoader.java @@ -84,6 +84,8 @@ import net.sourceforge.jnlp.security.AppVerifier; import net.sourceforge.jnlp.security.JNLPAppVerifier; import net.sourceforge.jnlp.security.PluginAppVerifier; import net.sourceforge.jnlp.security.SecurityDialogs; +import net.sourceforge.jnlp.security.appletextendedsecurity.AppletSecurityLevel; +import net.sourceforge.jnlp.security.appletextendedsecurity.AppletStartupSecuritySettings; import net.sourceforge.jnlp.security.appletextendedsecurity.UnsignedAppletTrustConfirmation; import net.sourceforge.jnlp.tools.JarCertVerifier; import net.sourceforge.jnlp.util.JarFile; @@ -115,6 +117,10 @@ public class JNLPClassLoader extends URLClassLoader { DOWNLOAD_TO_CACHE, REMOVE_FROM_CACHE, CHECK_CACHE } + public static enum SigningState { + FULL, PARTIAL, NONE + } + /** True if the application has a signed JNLP File */ private boolean isSignedJNLP = false; @@ -167,7 +173,7 @@ public class JNLPClassLoader extends URLClassLoader { /** the jar cert verifier tool to verify our jars */ private final JarCertVerifier jcv; - private boolean signing = false; + private SigningState signing = SigningState.NONE; /** ArrayList containing jar indexes for various jars available to this classloader */ private ArrayList jarIndexes = new ArrayList(); @@ -304,7 +310,7 @@ public class JNLPClassLoader extends URLClassLoader { * determine security settings here, after trying to verify jars. */ if (file instanceof PluginBridge) { - if (signing == true) { + if (getSigning()) { this.security = new SecurityDesc(file, SecurityDesc.ALL_PERMISSIONS, codebase.getHost()); @@ -327,13 +333,13 @@ public class JNLPClassLoader extends URLClassLoader { * Unsigned no Sandbox * */ - if (!file.getSecurity().getSecurityType().equals(SecurityDesc.SANDBOX_PERMISSIONS) && !signing) { + if (!file.getSecurity().getSecurityType().equals(SecurityDesc.SANDBOX_PERMISSIONS) && !getSigning()) { if (jcv.allJarsSigned()) { throw new LaunchException(file, null, R("LSFatal"), R("LCClient"), R("LSignedJNLPAppDifferentCerts"), R("LSignedJNLPAppDifferentCertsInfo")); } else { throw new LaunchException(file, null, R("LSFatal"), R("LCClient"), R("LUnsignedJarWithSecurity"), R("LUnsignedJarWithSecurityInfo")); } - } else if (signing == true) { + } else if (getSigning()) { this.security = file.getSecurity(); } else { this.security = new SecurityDesc(file, @@ -390,7 +396,7 @@ public class JNLPClassLoader extends URLClassLoader { JNLPClassLoader extLoader = uniqueKeyToLoader.get(uniqueKey); if (extLoader != null && extLoader != loader) { - if (loader.signing && !extLoader.signing) + if (loader.getSigning() && !extLoader.getSigning()) if (!SecurityDialogs.showNotAllSignedWarningDialog(file)) throw new LaunchException(file, null, R("LSFatal"), R("LCClient"), R("LSignedAppJarUsingUnsignedJar"), R("LSignedAppJarUsingUnsignedJarInfo")); @@ -609,8 +615,8 @@ public class JNLPClassLoader extends URLClassLoader { } } - if(allSigned) - signing = true; + if (allSigned) + signing = SigningState.FULL; //Check if main jar is found within extensions foundMainJar = foundMainJar || hasMainInExtensions(); @@ -676,7 +682,7 @@ public class JNLPClassLoader extends URLClassLoader { //Case when at least one jar has some signing if (jcv.isFullySigned()) { - signing = true; + signing = SigningState.FULL; // Check for main class in the downloaded jars, and check/verify signed JNLP fill checkForMain(initialJars); @@ -692,9 +698,10 @@ public class JNLPClassLoader extends URLClassLoader { boolean externalMainClass = (file.getLaunchInfo() != null && !foundMainJar && (available == null || available.size() == 0)); - if ((!jcv.allJarsSigned() || externalMainClass) && - !SecurityDialogs.showNotAllSignedWarningDialog(file)) - throw new LaunchException(file, null, R("LSFatal"), R("LCClient"), R("LSignedAppJarUsingUnsignedJar"), R("LSignedAppJarUsingUnsignedJarInfo")); + if (!jcv.allJarsSigned() || externalMainClass) { + checkNotAllSignedWithUser(file); + signing = SigningState.PARTIAL; + } // If main jar was found, but a signed JNLP file was not located if (!isSignedJNLP && foundMainJar) @@ -713,60 +720,69 @@ public class JNLPClassLoader extends URLClassLoader { // Otherwise this jar is simply unsigned -- make sure to ask // for permission on certain actions - signing = false; + signing = SigningState.NONE; } } + boolean containsSignedJar = false, containsUnsignedJar = false; for (JARDesc jarDesc : file.getResources().getJARs()) { - try { + File cachedFile; - File cachedFile; - - try { - cachedFile = tracker.getCacheFile(jarDesc.getLocation()); - } catch (IllegalResourceDescriptorException irde){ - //Caused by ignored resource being removed due to not being valid - OutputController.getLogger().log(OutputController.Level.ERROR_ALL, "JAR " + jarDesc.getLocation() + " is not a valid jar file. Continuing."); - continue; - } + try { + cachedFile = tracker.getCacheFile(jarDesc.getLocation()); + } catch (IllegalResourceDescriptorException irde) { + //Caused by ignored resource being removed due to not being valid + OutputController.getLogger().log(OutputController.Level.ERROR_ALL, "JAR " + jarDesc.getLocation() + " is not a valid jar file. Continuing."); + continue; + } - if (cachedFile == null) { - OutputController.getLogger().log(OutputController.Level.ERROR_ALL, "JAR " + jarDesc.getLocation() + " not found. Continuing."); - continue; // JAR not found. Keep going. - } + if (cachedFile == null) { + OutputController.getLogger().log(OutputController.Level.ERROR_ALL, "JAR " + jarDesc.getLocation() + " not found. Continuing."); + continue; // JAR not found. Keep going. + } - // TODO: Should be toURI().toURL() - URL location = cachedFile.toURL(); - SecurityDesc jarSecurity = file.getSecurity(); + SecurityDesc jarSecurity = file.getSecurity(); - if (file instanceof PluginBridge) { + if (file instanceof PluginBridge) { - URL codebase = null; + URL codebase = null; - if (file.getCodeBase() != null) { - codebase = file.getCodeBase(); - } else { - //Fixme: codebase should be the codebase of the Main Jar not - //the location. Although, it still works in the current state. - codebase = file.getResources().getMainJAR().getLocation(); - } + if (file.getCodeBase() != null) { + codebase = file.getCodeBase(); + } else { + //Fixme: codebase should be the codebase of the Main Jar not + //the location. Although, it still works in the current state. + codebase = file.getResources().getMainJAR().getLocation(); + } - if (signing) { + try { + if (JarCertVerifier.isJarSigned(jarDesc, new PluginAppVerifier(), tracker)) { + containsSignedJar = true; jarSecurity = new SecurityDesc(file, - SecurityDesc.ALL_PERMISSIONS, - codebase.getHost()); + SecurityDesc.ALL_PERMISSIONS, + codebase.getHost()); } else { + containsUnsignedJar = true; jarSecurity = new SecurityDesc(file, - SecurityDesc.SANDBOX_PERMISSIONS, - codebase.getHost()); + SecurityDesc.SANDBOX_PERMISSIONS, + codebase.getHost()); } + } catch (Exception e) { + OutputController.getLogger().log(e); + jarSecurity = new SecurityDesc(file, + SecurityDesc.SANDBOX_PERMISSIONS, + codebase.getHost()); } - - jarLocationSecurityMap.put(jarDesc.getLocation(), jarSecurity); - } catch (MalformedURLException mfe) { - OutputController.getLogger().log(OutputController.Level.ERROR_ALL, mfe); } + + jarLocationSecurityMap.put(jarDesc.getLocation(), jarSecurity); + } + + if (containsSignedJar && containsUnsignedJar) { + checkNotAllSignedWithUser(file); + signing = SigningState.PARTIAL; } + activateJars(initialJars); } @@ -1063,6 +1079,22 @@ public class JNLPClassLoader extends URLClassLoader { } } + /** + * Prompt the user to proceed on applets with mixed signing. + * @param file the JNLPFile or PluginBridge describing the applet/application to be launched + * @throws LaunchException if the user does not approve the prompt + */ + private void checkNotAllSignedWithUser(JNLPFile file) throws LaunchException { + boolean promptUser = true; + + if (JNLPRuntime.isTrustAll()) { + promptUser = false; + } + + if (promptUser && !SecurityDialogs.showNotAllSignedWarningDialog(file)) { + throw new LaunchException(file, null, R("LSFatal"), R("LCClient"), R("LSignedAppJarUsingUnsignedJar"), R("LSignedAppJarUsingUnsignedJarInfo")); + } + } /** * Add applet's codebase URL. This allows compatibility with * applets that load resources from their codebase instead of @@ -1864,7 +1896,7 @@ public class JNLPClassLoader extends URLClassLoader { } public boolean getSigning() { - return signing; + return signing == SigningState.FULL; } protected SecurityDesc getSecurity() { diff --git a/netx/net/sourceforge/jnlp/tools/JarCertVerifier.java b/netx/net/sourceforge/jnlp/tools/JarCertVerifier.java index 31270e8..0098e51 100644 --- a/netx/net/sourceforge/jnlp/tools/JarCertVerifier.java +++ b/netx/net/sourceforge/jnlp/tools/JarCertVerifier.java @@ -183,6 +183,14 @@ public class JarCertVerifier implements CertVerifier { return fullySigned; } + public static boolean isJarSigned(JARDesc jar, AppVerifier verifier, ResourceTracker tracker) throws Exception { + JarCertVerifier certVerifier = new JarCertVerifier(verifier); + List singleJarList = new ArrayList(); + singleJarList.add(jar); + certVerifier.add(singleJarList, tracker); + return certVerifier.allJarsSigned(); + } + /** * Update the verifier to consider new jars when verifying. * -- cgit v1.2.3