From 039ab180be9a600911e8f0893795de1f9b2d996a Mon Sep 17 00:00:00 2001 From: hns Date: Thu, 21 Nov 2002 15:55:32 +0000 Subject: [PATCH] merge in classloader branch --- src/helma/framework/core/AppClassLoader.java | 37 ++++++++ src/helma/framework/core/Application.java | 14 +-- .../framework/core/RequestEvaluator.java | 10 +- src/helma/framework/core/TypeManager.java | 26 ++++- src/helma/main/HelmaSecurityManager.java | 94 +++++++++++++++++++ ...etFactory.java => HelmaSocketFactory.java} | 4 +- src/helma/main/Server.java | 68 ++++++++++---- .../main/launcher/FilteredClassLoader.java | 36 +++++++ src/helma/main/launcher/Main.java | 94 ++++++++++++++----- 9 files changed, 319 insertions(+), 64 deletions(-) create mode 100644 src/helma/framework/core/AppClassLoader.java create mode 100644 src/helma/main/HelmaSecurityManager.java rename src/helma/main/{HopSocketFactory.java => HelmaSocketFactory.java} (90%) create mode 100644 src/helma/main/launcher/FilteredClassLoader.java diff --git a/src/helma/framework/core/AppClassLoader.java b/src/helma/framework/core/AppClassLoader.java new file mode 100644 index 00000000..b2ba2888 --- /dev/null +++ b/src/helma/framework/core/AppClassLoader.java @@ -0,0 +1,37 @@ +// ApplicationClassLoader.java + +package helma.framework.core; + +import java.net.URL; +import java.net.URLClassLoader; +import java.util.Enumeration; +import java.security.CodeSource; +import java.security.Permission; +import java.security.PermissionCollection; +import java.security.Permissions; + + +/** + * ClassLoader subclass with package accessible addURL method. + */ +public class AppClassLoader extends URLClassLoader { + + private final String appname; + + /** + * Create a HelmaClassLoader with the given application name and the given URLs + */ + public AppClassLoader(String appname, URL[] urls) { + super ( urls, AppClassLoader.class.getClassLoader()); + this.appname = appname; + } + + protected void addURL (URL url) { + super.addURL (url); + } + + public String getAppName () { + return appname; + } + +} diff --git a/src/helma/framework/core/Application.java b/src/helma/framework/core/Application.java index 25a5ddfa..ba49c454 100644 --- a/src/helma/framework/core/Application.java +++ b/src/helma/framework/core/Application.java @@ -17,6 +17,7 @@ import org.apache.xmlrpc.*; import java.util.*; import java.io.*; import java.net.URLEncoder; +import java.net.MalformedURLException; import java.lang.reflect.*; import java.rmi.*; import java.rmi.server.*; @@ -122,13 +123,6 @@ public final class Application implements IPathElement, Runnable { private String xmlrpcHandlerName; - /** - * Zero argument constructor needed for RMI - */ - /* public Application () throws RemoteException { - super (); - } */ - /** * Build an application with the given name in the app directory. No Server-wide * properties are created or used. @@ -224,7 +218,7 @@ public final class Application implements IPathElement, Runnable { /** * Get the application ready to run, initializing the evaluators and type manager. */ - public void init () throws DatabaseException, ScriptingException { + public void init () throws DatabaseException, ScriptingException, MalformedURLException { // scriptingEngine = new helma.scripting.fesi.FesiScriptingEnvironment (); // scriptingEngine.init (this, props); @@ -540,11 +534,11 @@ public final class Application implements IPathElement, Runnable { if (rootObject == null) { try { if ( classMapping.containsKey("root.factory.class") && classMapping.containsKey("root.factory.method") ) { - Class c = Class.forName( classMapping.getProperty("root.factory.class") ); + Class c = typemgr.loader.loadClass( classMapping.getProperty("root.factory.class") ); Method m = c.getMethod( classMapping.getProperty("root.factory.method"), null ); rootObject = m.invoke(c, null); } else { - Class c = Class.forName( classMapping.getProperty("root") ); + Class c = typemgr.loader.loadClass( classMapping.getProperty("root") ); rootObject = c.newInstance(); } } catch ( Exception e ) { diff --git a/src/helma/framework/core/RequestEvaluator.java b/src/helma/framework/core/RequestEvaluator.java index 4270a1d0..64d2e043 100644 --- a/src/helma/framework/core/RequestEvaluator.java +++ b/src/helma/framework/core/RequestEvaluator.java @@ -72,13 +72,11 @@ public final class RequestEvaluator implements Runnable { if (scriptingEngine == null) { String engineClassName = app.getProperty ( "scripting.engine.factory", - "helma.scripting.fesi.FesiEngineFactory"); + "helma.scripting.fesi.FesiEngine"); try { - Class clazz = Class.forName (engineClassName); - Class[] argClasses = {app.getClass(), getClass()}; - Method method = clazz.getMethod ("getEngine", argClasses); - Object[] args = {app, this}; - scriptingEngine = (ScriptingEngine) method.invoke (null, args); + Class clazz = app.typemgr.loader.loadClass (engineClassName); + scriptingEngine = (ScriptingEngine) clazz.newInstance (); + scriptingEngine.init (app, this); } catch (Exception x) { Throwable t = x; if (x instanceof InvocationTargetException) diff --git a/src/helma/framework/core/TypeManager.java b/src/helma/framework/core/TypeManager.java index 60b329ba..0ae4710b 100644 --- a/src/helma/framework/core/TypeManager.java +++ b/src/helma/framework/core/TypeManager.java @@ -9,7 +9,8 @@ import helma.scripting.*; import helma.util.*; import java.util.*; import java.io.*; - +import java.net.URL; +import java.net.MalformedURLException; /** * The type manager periodically checks the prototype definitions for its @@ -20,8 +21,9 @@ public final class TypeManager { Application app; File appDir; - HashMap prototypes; - HashMap zipfiles; + HashMap prototypes; // map of prototypes + HashMap zipfiles; // map of zipped script files + HashSet jarfiles; // set of Java archives long lastCheck = 0; long appDirMod = 0; // a checksum that changes whenever something in the application files changes. @@ -31,6 +33,9 @@ public final class TypeManager { Prototype hopobjectProto; // the global prototype Prototype globalProto; + + // app specific class loader, includes jar files in the app directory + AppClassLoader loader; final static String[] standardTypes = {"user", "global", "root", "hopobject"}; @@ -39,7 +44,7 @@ public final class TypeManager { final static String actionExtension = ".hac"; final static String skinExtension = ".skin"; - public TypeManager (Application app) { + public TypeManager (Application app) throws MalformedURLException { this.app = app; appDir = app.appDir; // make sure the directories for the standard prototypes exist, and lament otherwise @@ -54,6 +59,9 @@ public final class TypeManager { } prototypes = new HashMap (); zipfiles = new HashMap (); + jarfiles = new HashSet (); + URL helmajar = new URL ("file:"+app.home.getAbsolutePath()+"/lib/helma.jar"); + loader = new AppClassLoader(app.getName(), new URL[] { helmajar }); } @@ -111,6 +119,16 @@ public final class TypeManager { } continue; } + if (list[i].endsWith (".jar")) { + if (!jarfiles.contains (list[i])) { + jarfiles.add (list[i]); + File f = new File (appDir, list[i]); + try { + loader.addURL (new URL ("file:"+f.getAbsolutePath())); + } catch (MalformedURLException ignore) {} + } + continue; + } if (list[i].indexOf ('.') > -1) continue; Prototype proto = getPrototype (list[i]); diff --git a/src/helma/main/HelmaSecurityManager.java b/src/helma/main/HelmaSecurityManager.java new file mode 100644 index 00000000..e617e4ad --- /dev/null +++ b/src/helma/main/HelmaSecurityManager.java @@ -0,0 +1,94 @@ +// HelmaSecurityManager.java + +package helma.main; + +import java.security.Permission; +import java.io.FileDescriptor; +import java.net.InetAddress; +import java.util.HashSet; +import helma.framework.core.AppClassLoader; + +/** + * Liberal security manager for Helma system that makes sure application code + * is not allowed to exit the VM and set a security manager. + * + * This class can be subclassed to implement actual security policies. It contains + * a utility method getApplication that can be used to determine + * the name of the application trying to execute the action in question, if any. + */ +public class HelmaSecurityManager extends SecurityManager { + + // The set of actions forbidden to application code. + // We are pretty permissive, forbidding only System.exit() + // and setting the security manager. + private final static HashSet forbidden = new HashSet (); + static { + forbidden.add ("exitVM"); + forbidden.add ("setSecurityManager"); + } + + public void checkPermission(Permission p) { + if (p instanceof RuntimePermission) { + if (forbidden.contains (p.getName())) { + Class[] classes = getClassContext(); + for (int i=0; i 0) checkRunning (websrvPort); @@ -175,18 +208,6 @@ import org.apache.xmlrpc.*; System.exit (1); } - - // get main property file from home dir or vice versa, depending on what we have. - // get property file from hopHome - if (propfile == null) { - if (homeDir != null) - propfile = new File (homeDir, "server.properties").getAbsolutePath (); - else - propfile = new File ("server.properties").getAbsolutePath (); - } - // create system properties - sysProps = new SystemProperties (propfile); - // get hopHome from property file if (homeDir == null) homeDir = sysProps.getProperty ("hophome"); @@ -343,7 +364,7 @@ import org.apache.xmlrpc.*; if (rmiPort > 0) { if (paranoid) { - HopSocketFactory factory = new HopSocketFactory (); + HelmaSocketFactory factory = new HelmaSocketFactory (); String rallow = sysProps.getProperty ("allowWeb"); if (rallow != null) { StringTokenizer st = new StringTokenizer (rallow, " ,;"); @@ -370,6 +391,19 @@ import org.apache.xmlrpc.*; return; } + // set the security manager. + // the default implementation is helma.main.HelmaSecurityManager. + try { + String secManClass = sysProps.getProperty ("securityManager"); + if (secManClass != null) { + SecurityManager secMan = (SecurityManager) Class.forName(secManClass).newInstance (); + System.setSecurityManager (secMan); + getLogger().log ("Setting security manager to "+secManClass); + } + } catch (Exception x) { + getLogger().log ("Error setting security manager: "+x); + } + // start applications appManager.startAll (); diff --git a/src/helma/main/launcher/FilteredClassLoader.java b/src/helma/main/launcher/FilteredClassLoader.java new file mode 100644 index 00000000..a6587bdd --- /dev/null +++ b/src/helma/main/launcher/FilteredClassLoader.java @@ -0,0 +1,36 @@ +// FilteredClassLoader.java + +package helma.main.launcher; + +import java.net.URL; +import java.net.URLClassLoader; +import java.util.Hashtable; +import java.security.PermissionCollection; +import java.security.Permissions; +import java.security.Policy; +import java.security.CodeSource; + +/** + * ClassLoader that is able to exclude certain packages from loading. + */ +public class FilteredClassLoader extends URLClassLoader { + + /** + * Create a server wide class loader that doesn't see the scripting engine(s) + * embedded in helma.jar. These files should be loaded by the per-application + * class loaders so that special security policies can be applied to them and + * so that they can load classes from jar files in the app directories. + */ + public FilteredClassLoader(URL[] urls) { + super (urls); + } + + /** + * Mask classes that implement the scripting engine(s) contained in helma.jar + */ + protected Class findClass (String name) throws ClassNotFoundException { + if (name != null && (name.startsWith ("helma.scripting.fesi") || name.startsWith ("FESI"))) + throw new ClassNotFoundException (name); + return super.findClass (name); + } +} diff --git a/src/helma/main/launcher/Main.java b/src/helma/main/launcher/Main.java index 96abb1fc..d3b62dcc 100644 --- a/src/helma/main/launcher/Main.java +++ b/src/helma/main/launcher/Main.java @@ -7,6 +7,8 @@ import java.net.URL; import java.io.File; import java.io.FilenameFilter; import java.lang.reflect.Method; +import java.util.ArrayList; +import java.security.Policy; /** * Helma bootstrap class. Figures out Helma home directory, sets up class path and @@ -14,44 +16,86 @@ import java.lang.reflect.Method; */ public class Main { + public static final String[] jars = { "helma.jar", "jetty.jar", "crimson.jar", "xmlrpc.jar", + "village.jar", "servlet.jar", "regexp.jar", "mail.jar", + "activation.jar", "netcomponents.jar", "jimi.jar", + "apache-dom.jar", "jdom.jar"}; + public static void main (String[] args) throws Exception { - // try to get Helma installation directory - try { - URLClassLoader apploader = (URLClassLoader) Main.class.getClassLoader(); - // (URLClassLoader) Thread.currentThread().getContextClassLoader(); - URL homeUrl = apploader.findResource("helma/main/launcher/Main.class"); - String home = homeUrl.toString().substring(4); - int excl = home.indexOf ("!"); - if (excl > -1) { - home = home.substring(0, excl); - homeUrl = new URL (home); - File f = new File (homeUrl.getPath()); - f = f.getParentFile(); - System.err.println ("GOT HOME: "+f); - } - } catch (Exception ignore) { - // unable to get Helma home dir from launcher jar + // check if home directory is set via command line arg. If not, + // we'll get it from the location of the jar file this class + // has been loaded from. + String home = null; + // first, try to get helma home dir from command line options + for (int i=0; i!/{entry} + // we strip away the jar: prefix and the !/{entry} suffix + // to get the original jar file URL + home = homeUrl.toString().substring(4); + int excl = home.indexOf ("!"); + if (excl > -1) { + home = home.substring(0, excl); + homeUrl = new URL (home); + File f = new File (homeUrl.getPath()); + home = f.getParent(); + // add home dir to the command line arguments + String[] newArgs = new String [args.length+2]; + newArgs[0] = "-h"; + newArgs[1] = home; + System.arraycopy (args, 0, newArgs, 2, args.length); + args = newArgs; + } + } catch (Exception ignore) { + // unable to get Helma home dir from launcher jar + } + } + // set the current working directory to the helma home dir. + // note that this is not a real cwd, which is not supported + // by java. It makes sure relative to absolute path name + // conversion is done right, so for Helma code, this should + // work. + System.setProperty ("user.dir", home); + + // set up the class path + File libdir = new File (home, "lib"); + ArrayList jarlist = new ArrayList (); + for (int i=0;i