Mercurial > hg > release > icedtea-web-1.5
changeset 901:2bb356915cc1
Introduce SecurityDelegate
* netx/net/sourceforge/jnlp/runtime/JNLPClassLoader.java:
(SecurityDelegate, SecurityDelegateImpl) new interface and implementation.
(initializeResources, setSecurity, activateJars, addNewJar) refactored to
use SecurityDelegate
author | Andrew Azores <aazores@redhat.com> |
---|---|
date | Fri, 28 Feb 2014 16:33:48 -0500 |
parents | 1fb5b82415ce |
children | ededca6b0659 |
files | ChangeLog netx/net/sourceforge/jnlp/runtime/JNLPClassLoader.java |
diffstat | 2 files changed, 176 insertions(+), 98 deletions(-) [+] |
line wrap: on
line diff
--- a/ChangeLog Thu Feb 27 14:35:41 2014 -0500 +++ b/ChangeLog Fri Feb 28 16:33:48 2014 -0500 @@ -1,3 +1,10 @@ +2014-02-28 Andrew Azores <aazores@redhat.com> + + * netx/net/sourceforge/jnlp/runtime/JNLPClassLoader.java: + (SecurityDelegate, SecurityDelegateImpl) new interface and implementation. + (initializeResources, setSecurity, activateJars, addNewJar) refactored to + use SecurityDelegate + 2014-02-27 Andrew Azores <aazores@redhat.com> * netx/net/sourceforge/jnlp/runtime/JNLPClassLoader.java: treat signed
--- a/netx/net/sourceforge/jnlp/runtime/JNLPClassLoader.java Thu Feb 27 14:35:41 2014 -0500 +++ b/netx/net/sourceforge/jnlp/runtime/JNLPClassLoader.java Fri Feb 28 16:33:48 2014 -0500 @@ -224,6 +224,8 @@ private boolean enableCodeBase = false; + private final SecurityDelegate securityDelegate; + /** * Create a new JNLPClassLoader from the specified file. * @@ -272,6 +274,8 @@ addToCodeBaseLoader(this.file.getCodeBase()); } + this.securityDelegate = new SecurityDelegateImpl(this); + // initialize extensions initializeExtensions(); @@ -309,52 +313,8 @@ } private void setSecurity() throws LaunchException { - URL codebase = guessCodeBase(); - - /** - * When we're trying to load an applet, file.getSecurity() will return - * null since there is no jnlp file to specify permissions. We - * determine security settings here, after trying to verify jars. - */ - if (file instanceof PluginBridge) { - if (getSigning()) { - this.security = new SecurityDesc(file, - SecurityDesc.ALL_PERMISSIONS, - codebase.getHost()); - } else { - this.security = new SecurityDesc(file, - SecurityDesc.SANDBOX_PERMISSIONS, - codebase.getHost()); - } - } else { //regular jnlp file - - /* - * Various combinations of the jars being signed and <security> tags being - * present are possible. They are treated as follows - * - * Jars JNLP File Result - * - * Signed <security> Appropriate Permissions - * Signed no <security> Sandbox - * Unsigned <security> Error - * Unsigned no <security> Sandbox - * - */ - 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 (getSigning()) { - this.security = file.getSecurity(); - } else { - this.security = new SecurityDesc(file, - SecurityDesc.SANDBOX_PERMISSIONS, - codebase.getHost()); - } - } + this.security = securityDelegate.getClassLoaderSecurity(codebase.getHost()); } /** @@ -766,38 +726,20 @@ continue; // JAR not found. Keep going. } - SecurityDesc jarSecurity = file.getSecurity(); - - if (file instanceof PluginBridge) { - - 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(); - } - - try { - if (JarCertVerifier.isJarSigned(jarDesc, new PluginAppVerifier(), tracker)) { - containsSignedJar = true; - jarSecurity = new SecurityDesc(file, - SecurityDesc.ALL_PERMISSIONS, - codebase.getHost()); - } else { - containsUnsignedJar = true; - jarSecurity = new SecurityDesc(file, - SecurityDesc.SANDBOX_PERMISSIONS, - codebase.getHost()); - } - } catch (Exception e) { - OutputController.getLogger().log(e); - jarSecurity = new SecurityDesc(file, - SecurityDesc.SANDBOX_PERMISSIONS, - codebase.getHost()); - } + final URL codebase; + 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(); + } + + final SecurityDesc jarSecurity = securityDelegate.getCodebaseSecurityDesc(jarDesc, codebase.getHost()); + if (jarSecurity.getSecurityType().equals(SecurityDesc.SANDBOX_PERMISSIONS)) { + containsUnsignedJar = true; + } else { + containsSignedJar = true; } jarLocationSecurityMap.put(jarDesc.getLocation(), jarSecurity); @@ -1326,17 +1268,7 @@ codebase = file.getResources().getMainJAR().getLocation(); } - SecurityDesc jarSecurity = null; - if (jcv.isFullySigned()) { - // Already trust application, nested jar should be given - jarSecurity = new SecurityDesc(file, - SecurityDesc.ALL_PERMISSIONS, - codebase.getHost()); - } else { - jarSecurity = new SecurityDesc(file, - SecurityDesc.SANDBOX_PERMISSIONS, - codebase.getHost()); - } + final SecurityDesc jarSecurity = securityDelegate.getJarPermissions(codebase.getHost()); try { URL fileURL = new URL("file://" + extractedJarLocation); @@ -1659,16 +1591,7 @@ checkTrustWithUser(); - final SecurityDesc security; - if (jcv.isFullySigned()) { - security = new SecurityDesc(file, - SecurityDesc.ALL_PERMISSIONS, - file.getCodeBase().getHost()); - } else { - security = new SecurityDesc(file, - SecurityDesc.SANDBOX_PERMISSIONS, - file.getCodeBase().getHost()); - } + final SecurityDesc security = securityDelegate.getJarPermissions(file.getCodeBase().getHost()); jarLocationSecurityMap.put(remoteURL, security); @@ -2346,6 +2269,154 @@ } + /** + * SecurityDelegate, in real usage, relies on having a "parent" JNLPClassLoader instance. + * However, JNLPClassLoaders are very large, heavyweight, difficult-to-mock objects, which + * means that unit testing on anything that uses a SecurityDelegate can become very difficult. + * For example, JarCertVerifier is designed separated from the ClassLoader so it can be tested + * in isolation. However, JCV needs some sort of access back to JNLPClassLoader instances to + * be able to invoke setRunInSandbox(). The SecurityDelegate handles this, allowing JCV to be + * tested without instantiating JNLPClassLoaders, by creating a fake SecurityDelegate that does + * not require one. + */ + public static interface SecurityDelegate { + public boolean isPluginApplet(); + + public boolean userPromptedForSandbox(); + + public SecurityDesc getCodebaseSecurityDesc(final JARDesc jarDesc, final String codebaseHost); + + public SecurityDesc getClassLoaderSecurity(final String codebaseHost) throws LaunchException; + + public SecurityDesc getJarPermissions(final String codebaseHost); + + public void setRunInSandbox() throws LaunchException; + + public boolean getRunInSandbox(); + } + + /** + * Handles security decision logic for the JNLPClassLoader, eg which permission level to assign + * to JARs. + */ + public static class SecurityDelegateImpl implements SecurityDelegate { + private final JNLPClassLoader classLoader; + private boolean runInSandbox; + private boolean promptedForSandbox; + + public SecurityDelegateImpl(final JNLPClassLoader classLoader) { + this.classLoader = classLoader; + runInSandbox = false; + promptedForSandbox = false; + } + + public boolean isPluginApplet() { + return classLoader.file instanceof PluginBridge; + } + + public SecurityDesc getCodebaseSecurityDesc(final JARDesc jarDesc, final String codebaseHost) { + if (runInSandbox) { + return new SecurityDesc(classLoader.file, + SecurityDesc.SANDBOX_PERMISSIONS, + codebaseHost); + } else { + if (isPluginApplet()) { + try { + if (JarCertVerifier.isJarSigned(jarDesc, new PluginAppVerifier(), classLoader.tracker)) { + return new SecurityDesc(classLoader.file, + SecurityDesc.ALL_PERMISSIONS, + codebaseHost); + } else { + return new SecurityDesc(classLoader.file, + SecurityDesc.SANDBOX_PERMISSIONS, + codebaseHost); + } + } catch (final Exception e) { + OutputController.getLogger().log(e); + return new SecurityDesc(classLoader.file, + SecurityDesc.SANDBOX_PERMISSIONS, + codebaseHost); + } + } else { + return classLoader.file.getSecurity(); + } + } + } + + public SecurityDesc getClassLoaderSecurity(final String codebaseHost) throws LaunchException { + if (isPluginApplet()) { + if (!runInSandbox && classLoader.getSigning()) { + return new SecurityDesc(classLoader.file, + SecurityDesc.ALL_PERMISSIONS, + codebaseHost); + } else { + return new SecurityDesc(classLoader.file, + SecurityDesc.SANDBOX_PERMISSIONS, + codebaseHost); + } + } else { + /* + * Various combinations of the jars being signed and <security> tags being + * present are possible. They are treated as follows + * + * Jars JNLP File Result + * + * Signed <security> Appropriate Permissions + * Signed no <security> Sandbox + * Unsigned <security> Error + * Unsigned no <security> Sandbox + * + */ + if (!runInSandbox && !classLoader.getSigning() + && !classLoader.file.getSecurity().getSecurityType().equals(SecurityDesc.SANDBOX_PERMISSIONS)) { + if (classLoader.jcv.allJarsSigned()) { + throw new LaunchException(classLoader.file, null, R("LSFatal"), R("LCClient"), R("LSignedJNLPAppDifferentCerts"), R("LSignedJNLPAppDifferentCertsInfo")); + } else { + throw new LaunchException(classLoader.file, null, R("LSFatal"), R("LCClient"), R("LUnsignedJarWithSecurity"), R("LUnsignedJarWithSecurityInfo")); + } + } else if (!runInSandbox && classLoader.getSigning()) { + return classLoader.file.getSecurity(); + } else { + return new SecurityDesc(classLoader.file, + SecurityDesc.SANDBOX_PERMISSIONS, + codebaseHost); + } + } + } + + public SecurityDesc getJarPermissions(final String codebaseHost) { + if (!runInSandbox && classLoader.jcv.isFullySigned()) { + // Already trust application, nested jar should be given + return new SecurityDesc(classLoader.file, + SecurityDesc.ALL_PERMISSIONS, + codebaseHost); + } else { + return new SecurityDesc(classLoader.file, + SecurityDesc.SANDBOX_PERMISSIONS, + codebaseHost); + } + } + + public void setRunInSandbox() throws LaunchException { + if (promptedForSandbox || classLoader.security != null + || classLoader.jarLocationSecurityMap.size() != 0) { + throw new LaunchException(classLoader.file, null, R("LSFatal"), R("LCInit"), R("LRunInSandboxError"), R("LRunInSandboxErrorInfo")); + } + + this.promptedForSandbox = true; + this.runInSandbox = true; + } + + public boolean getRunInSandbox() { + return this.runInSandbox; + } + + public boolean userPromptedForSandbox() { + return this.promptedForSandbox; + } + + } + /* * Helper class to expose protected URLClassLoader methods. * Classes loaded from the codebase are absolutely NOT signed, by definition!