New security implementation.

This commit is contained in:
igor%mir2.org 2002-07-04 21:40:12 +00:00
parent 6909994c7b
commit 988587183a
19 changed files with 642 additions and 315 deletions

View File

@ -1,6 +1,7 @@
apiClasses=\
src/org/mozilla/javascript/ClassDefinitionException.java,\
src/org/mozilla/javascript/ClassOutput.java,\
src/org/mozilla/javascript/ClassShutter.java,\
src/org/mozilla/javascript/Context.java,\
src/org/mozilla/javascript/ContextListener.java,\
src/org/mozilla/javascript/EcmaError.java,\
@ -15,6 +16,7 @@ apiClasses=\
src/org/mozilla/javascript/Scriptable.java,\
src/org/mozilla/javascript/ScriptableObject.java,\
src/org/mozilla/javascript/SecuritySupport.java,\
src/org/mozilla/javascript/SecurityController.java,\
src/org/mozilla/javascript/WrapFactory.java,\
src/org/mozilla/javascript/WrapHandler.java,\
src/org/mozilla/javascript/Wrapper.java,\

View File

@ -38,10 +38,15 @@ This interface can be implemented to control the actions the JavaScript
engine takes when it encounters errors.</li>
<li>
<a href="org/mozilla/javascript/SecuritySupport.html">SecuritySupport</a>
<a href="org/mozilla/javascript/SecurityController.html">SecurityController</a>
- Optional support routines that must be provided by embeddings implementing
security controls on scripts.</li>
<li>
<a href="org/mozilla/javascript/ClassShutter.html">ClassShutter</a>
- Embeddings that wish to filter Java classes that are visible to scripts
through the LiveConnect, should implement this interface.</li>
<li>
<a href="org/mozilla/javascript/Wrapper.html">Wrapper</a> - Interface implemented
by objects wrapping other objects. Provides a method for recovering the

View File

@ -350,7 +350,6 @@ public class BaseFunction extends IdScriptable implements Function {
}
String sourceName = filename+'#'+linep[0]+"(Function)";
Object securityDomain = cx.getSecurityDomainForStackDepth(4);
Scriptable global = ScriptableObject.getTopLevelScope(scope);
// Compile the function with opt level of -1 to force interpreter
@ -361,7 +360,7 @@ public class BaseFunction extends IdScriptable implements Function {
try {
fn = (NativeFunction) cx.compileFunction(global, source,
sourceName, 1,
securityDomain);
null);
}
finally { cx.setOptimizationLevel(oldOptLevel); }

View File

@ -0,0 +1,86 @@
/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* The contents of this file are subject to the Netscape Public
* License Version 1.1 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.mozilla.org/NPL/
*
* Software distributed under the License is distributed on an "AS
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
* implied. See the License for the specific language governing
* rights and limitations under the License.
*
* The Original Code is Rhino code, released
* May 6, 1999.
*
* The Initial Developer of the Original Code is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1997-1999 Netscape Communications Corporation. All
* Rights Reserved.
*
* Contributor(s):
* Norris Boyd
* Igor Bukanov
*
* Alternatively, the contents of this file may be used under the
* terms of the GNU Public License (the "GPL"), in which case the
* provisions of the GPL are applicable instead of those above.
* If you wish to allow use of your version of this file only
* under the terms of the GPL and not to allow others to use your
* version of this file under the NPL, indicate your decision by
* deleting the provisions above and replace them with the notice
* and other provisions required by the GPL. If you do not delete
* the provisions above, a recipient may use your version of this
* file under either the NPL or the GPL.
*/
// API class
package org.mozilla.javascript;
/**
Embeddings that wish to filter Java classes that are visible to scripts
through the LiveConnect, should implement this interface.
@see Context#setClassShutter(ClassShutter)
@since 1.5 Release 4
@author Norris Boyd
*/
public interface ClassShutter {
/**
* Return true iff the Java class with the given name should be exposed
* to scripts.
* <p>
* An embedding may filter which Java classes are exposed through
* LiveConnect to JavaScript scripts.
* <p>
* Due to the fact that there is no package reflection in Java,
* this method will also be called with package names. There
* is no way for Rhino to tell if "Packages.a.b" is a package name
* or a class that doesn't exist. What Rhino does is attempt
* to load each segment of "Packages.a.b.c": It first attempts to
* load class "a", then attempts to load class "a.b", then
* finally attempts to load class "a.b.c". On a Rhino installation
* without any ClassShutter set, and without any of the
* above classes, the expression "Packages.a.b.c" will result in
* a [JavaPackage a.b.c] and not an error.
* <p>
* With ClassShutter supplied, Rhino will first call
* visibleToScripts before attempting to look up the class name. If
* visibleToScripts returns false, the class name lookup is not
* performed and subsequent Rhino execution assumes the class is
* not present. So for "java.lang.System.out.println" the lookup
* of "java.lang.System" is skipped and thus Rhino assumes that
* "java.lang.System" doesn't exist. So then for "java.lang.System.out",
* Rhino attempts to load the class "java.lang.System.out" because
* it assumes that "java.lang.System" is a package name.
* <p>
* @param fullClassName the full name of the class (including the package
* name, with '.' as a delimiter). For example the
* standard string class is "java.lang.String"
* @return whether or not to reveal this class to scripts
*/
public boolean visibleToScripts(String fullClassName);
}

View File

@ -96,22 +96,6 @@ public class Context {
* @see org.mozilla.javascript.Context#enter
*/
public Context() {
init();
}
/**
* Create a new context with the associated security support.
*
* @param securitySupport an encapsulation of the functionality
* needed to support security for scripts.
* @see org.mozilla.javascript.SecuritySupport
*/
public Context(SecuritySupport securitySupport) {
this.securitySupport = securitySupport;
init();
}
private void init() {
setLanguageVersion(VERSION_DEFAULT);
optimizationLevel = codegenClass != null ? 0 : -1;
Object[] array = contextListeners;
@ -122,6 +106,14 @@ public class Context {
}
}
/**
@deprecated The {@link SecuritySupport} class is deprecated. See its documentation for the upgrade path.
*/
public Context(SecuritySupport x) {
this();
setClassShutter(x);
}
/**
* Get a context associated with the current thread, creating
* one if need be.
@ -1473,54 +1465,48 @@ public class Context {
}
/**
* Set the security support for this context.
* <p> SecuritySupport may only be set if it is currently null.
* Set the security controller for this context.
* <p> SecurityController may only be set if it is currently null.
* Otherwise a SecurityException is thrown.
* @param supportObj a SecuritySupport object
* @throws SecurityException if there is already a SecuritySupport
* @param controller a SecurityController object
* @throws SecurityException if there is already a SecurityController
* object for this Context
*/
public synchronized void setSecuritySupport(SecuritySupport supportObj) {
if (securitySupport != null) {
public void setSecurityController(SecurityController controller) {
if (controller == null) throw new IllegalArgumentException();
if (securityController != null) {
throw new SecurityException("Cannot overwrite existing " +
"SecuritySupport object");
"SecurityController object");
}
securitySupport = supportObj;
securityController = controller;
}
/**
@deprecated The {@link SecuritySupport} class is deprecated. See its documentation for the upgrade path.
*/
public void setSecuritySupport(SecuritySupport x) {
setClassShutter(x);
}
/**
* Return true if a security domain is required on calls to
* compile and evaluate scripts.
*
* @since 1.4 Release 2
* Set the LiveConnect access filter for this context.
* <p> {@link ClassShutter} may only be set if it is currently null.
* Otherwise a SecurityException is thrown.
* @param shutter a ClassShutter object
* @throws SecurityException if there is already a ClassShutter
* object for this Context
*/
public static boolean isSecurityDomainRequired() {
return requireSecurityDomain;
public void setClassShutter(ClassShutter shutter) {
if (shutter == null) throw new IllegalArgumentException();
if (classShutter != null) {
throw new SecurityException("Cannot overwrite existing " +
"ClassShutter object");
}
classShutter = shutter;
}
/**
* Returns the security context associated with the innermost
* script or function being executed by the interpreter.
* @since 1.4 release 2
*/
public Object getInterpreterSecurityDomain() {
return interpreterSecurityDomain;
}
/**
* Returns true if the class parameter is a class in the
* interpreter. Typically used by embeddings that get a class
* context to check security. These embeddings must know
* whether to get the security context associated with the
* interpreter or not.
*
* @param cl a class to test whether or not it is an interpreter
* class
* @return true if cl is an interpreter class
* @since 1.4 release 2
*/
public boolean isInterpreterClass(Class cl) {
return cl == Interpreter.class;
ClassShutter getClassShutter() {
return classShutter;
}
/**
@ -1943,11 +1929,17 @@ public class Context {
boolean returnFunction)
throws IOException
{
Object dynamicDoamin = null;
if (securityController != null) {
dynamicDoamin = securityController.
getDynamicSecurityDomain(securityDomain);
}
if (debugger != null && in != null) {
in = new DebugReader(in);
}
TokenStream ts = new TokenStream(in, scope, sourceName, lineno);
return compile(scope, ts, securityDomain, in, returnFunction);
return compile(scope, ts, dynamicDoamin, in, returnFunction);
}
private static Class codegenClass;
@ -2001,7 +1993,7 @@ public class Context {
}
private Object compile(Scriptable scope, TokenStream ts,
Object securityDomain, Reader in,
Object dynamicSecurityDomain, Reader in,
boolean returnFunction)
throws IOException
{
@ -2038,8 +2030,10 @@ public class Context {
tree.putProp(Node.DEBUGSOURCE_PROP, dr.getSaved());
}
Object result = compiler.compile(this, scope, tree, securityDomain,
securitySupport, nameHelper);
Object result = compiler.compile(this, scope, tree,
dynamicSecurityDomain,
securityController,
nameHelper);
return errorCount == 0 ? result : null;
}
@ -2120,81 +2114,15 @@ public class Context {
return version == VERSION_DEFAULT || version >= VERSION_1_3;
}
/**
* Get the security context from the given class.
* <p>
* When some form of security check needs to be done, the class context
* must retrieved from the security manager to determine what class is
* requesting some form of privileged access.
* @since 1.4 release 2
*/
Object getSecurityDomainFromClass(Class cl) {
if (cl == Interpreter.class)
return interpreterSecurityDomain;
return securitySupport.getSecurityDomain(cl);
}
SecuritySupport getSecuritySupport() {
return securitySupport;
}
Object getSecurityDomainForStackDepth(int depth) {
Object result = null;
if (securitySupport != null) {
Class[] classes = securitySupport.getClassContext();
if (classes != null) {
if (depth != -1) {
int depth1 = depth + 1;
result = getSecurityDomainFromClass(classes[depth1]);
} else {
for (int i=1; i < classes.length; i++) {
result = getSecurityDomainFromClass(classes[i]);
if (result != null)
break;
}
}
}
}
if (result != null)
return result;
if (requireSecurityDomain)
checkSecurityDomainRequired();
return null;
}
private static boolean requireSecurityDomain = true;
private static boolean resourceMissing = false;
final static String securityResourceName =
"org.mozilla.javascript.resources.Security";
static {
try {
ResourceBundle rb = ResourceBundle.getBundle(securityResourceName);
String s = rb.getString("security.requireSecurityDomain");
requireSecurityDomain = s.equals("true");
} catch (java.util.MissingResourceException mre) {
requireSecurityDomain = true;
resourceMissing = true;
} catch (SecurityException se) {
requireSecurityDomain = true;
}
}
final public static void checkSecurityDomainRequired() {
if (requireSecurityDomain) {
String msg = "Required security context not found";
if (resourceMissing) {
msg += ". Didn't find properties file at " +
securityResourceName;
}
throw new SecurityException(msg);
}
// Should not be public
SecurityController getSecurityController() {
return securityController;
}
public boolean isGeneratingDebugChanged() {
return generatingDebugChanged;
}
/**
* Add a name to the list of names forcing the creation of real
* activation objects for functions.
@ -2259,7 +2187,8 @@ public class Context {
int version;
int errorCount;
private SecuritySupport securitySupport;
private SecurityController securityController;
private ClassShutter classShutter;
private ErrorReporter errorReporter;
private Thread currentThread;
private RegExpProxy regExpProxy;

View File

@ -72,9 +72,8 @@ public class Interpreter {
public Object compile(Context cx, Scriptable scope, Node tree,
Object securityDomain,
SecuritySupport securitySupport,
SecurityController securityController,
ClassNameHelper nameHelper)
throws IOException
{
version = cx.getLanguageVersion();
itsData = new InterpreterData(cx, securityDomain);
@ -1528,15 +1527,8 @@ public class Interpreter {
throws JavaScriptException
{
if (cx.interpreterSecurityDomain != idata.securityDomain) {
// If securityDomain is different, update domain in Cotext
// and call self under new domain
Object savedDomain = cx.interpreterSecurityDomain;
cx.interpreterSecurityDomain = idata.securityDomain;
try {
return interpret(cx, scope, thisObj, args, fnOrScript, idata);
} finally {
cx.interpreterSecurityDomain = savedDomain;
}
return execWithNewDomain(cx, scope, thisObj, args, fnOrScript,
idata);
}
final Object DBL_MRK = Interpreter.DBL_MRK;
@ -2787,6 +2779,35 @@ public class Interpreter {
activation.put(name, activation, value);
}
private static Object execWithNewDomain(Context cx, Scriptable scope,
final Scriptable thisObj,
final Object[] args,
final NativeFunction fnOrScript,
final InterpreterData idata)
throws JavaScriptException
{
if (cx.interpreterSecurityDomain == idata.securityDomain)
Context.codeBug();
Script code = new Script() {
public Object exec(Context cx, Scriptable scope)
throws JavaScriptException
{
return interpret(cx, scope, thisObj, args, fnOrScript, idata);
}
};
Object savedDomain = cx.interpreterSecurityDomain;
cx.interpreterSecurityDomain = idata.securityDomain;
try {
return cx.getSecurityController().
execWithDomain(cx, scope, code, idata.securityDomain);
}finally {
cx.interpreterSecurityDomain = savedDomain;
}
}
private boolean itsInFunctionFlag;
private InterpreterData itsData;

View File

@ -53,8 +53,6 @@ class InterpreterData implements Serializable {
itsStringTable = new String[INITIAL_STRINGTABLE_SIZE];
itsUseDynamicScope = cx.hasCompileFunctionsWithDynamicScope();
if (securityDomain == null)
Context.checkSecurityDomainRequired();
this.securityDomain = securityDomain;
}

View File

@ -334,10 +334,10 @@ public class JavaAdapter extends ScriptableObject {
}
}
SecuritySupport ss = cx.getSecuritySupport();
if (ss != null) {
Object securityDomain = cx.getSecurityDomainForStackDepth(-1);
Class result = ss.defineClass(adapterName, bytes, securityDomain);
SecurityController sc = cx.getSecurityController();
if (sc != null) {
Object securityDomain = sc.getDynamicSecurityDomain(null);
Class result = sc.defineClass(adapterName, bytes, securityDomain);
if (result != null)
return result;
}

View File

@ -495,14 +495,13 @@ public class NativeGlobal implements IdFunctionMaster {
try {
StringReader in = new StringReader((String) x);
Object securityDomain = cx.getSecurityDomainForStackDepth(3);
// Compile the reader with opt level of -1 to force interpreter
// mode.
int oldOptLevel = cx.getOptimizationLevel();
cx.setOptimizationLevel(-1);
Script script = cx.compileReader(scope, in, sourceName, 1,
securityDomain);
null);
cx.setOptimizationLevel(oldOptLevel);
// if the compile fails, an error has been reported by the

View File

@ -68,7 +68,7 @@ public class NativeJavaPackage extends ScriptableObject {
"java.applet",
};
public static class TopLevelPackage extends NativeJavaPackage
public static class TopLevelPackage extends NativeJavaPackage
implements Function
{
public TopLevelPackage() {
@ -209,10 +209,10 @@ public class NativeJavaPackage extends ScriptableObject {
? name
: packageName + "." + name;
Context cx = Context.getContext();
SecuritySupport ss = cx.getSecuritySupport();
ClassShutter shutter = cx.getClassShutter();
Scriptable newValue;
try {
if (ss != null && !ss.visibleToScripts(newPackage))
if (shutter != null && !shutter.visibleToScripts(newPackage))
throw new ClassNotFoundException();
Class newClass = classLoader != null
? classLoader.loadClass(newPackage)

View File

@ -159,10 +159,7 @@ public class NativeScript extends NativeFunction implements Script {
filename = "<Script object>";
linep[0] = 1;
}
Object securityDomain =
cx.getSecurityDomainForStackDepth(5);
return cx.compileReader(scope, reader, filename, linep[0],
securityDomain);
return cx.compileReader(scope, reader, filename, linep[0], null);
}
catch (IOException e) {
throw new RuntimeException("Unexpected IOException");

View File

@ -0,0 +1,107 @@
/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* The contents of this file are subject to the Netscape Public
* License Version 1.1 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.mozilla.org/NPL/
*
* Software distributed under the License is distributed on an "AS
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
* implied. See the License for the specific language governing
* rights and limitations under the License.
*
* The Original Code is Rhino code, released
* May 6, 1999.
*
* The Initial Developer of the Original Code is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1997-1999 Netscape Communications Corporation. All
* Rights Reserved.
*
* Contributor(s):
* Norris Boyd
* Igor Bukanov
*
* Alternatively, the contents of this file may be used under the
* terms of the GNU Public License (the "GPL"), in which case the
* provisions of the GPL are applicable instead of those above.
* If you wish to allow use of your version of this file only
* under the terms of the GPL and not to allow others to use your
* version of this file under the NPL, indicate your decision by
* deleting the provisions above and replace them with the notice
* and other provisions required by the GPL. If you do not delete
* the provisions above, a recipient may use your version of this
* file under either the NPL or the GPL.
*/
// API class
package org.mozilla.javascript;
/**
* This class describes the support needed to implement security.
* <p>
* Three main pieces of functionality are required to implement
* security for JavaScript. First, it must be possible to define
* classes with an associated security domain. (This security
* domain may be any object incorporating notion of access
* restrictions that has meaning to an embedding; for a client-side
* JavaScript embedding this would typically be
* java.security.ProtectionDomain or similar object depending on an
* origin URL and/or a digital certificate.)
* Next it must be possible to get a security domain object that
* allows a particular action only if all security domains
* associated with code on the current Java stack allows it. And
* finally, it must be possible to execute script code with
* associated security domain injected into Java stack.
* <p>
* These three pieces of functionality are encapsulated in the
* SecurityController class.
*
* @see org.mozilla.javascript.Context#setSecurityController(SecurityController)
* @see java.lang.ClassLoader
* @since 1.5 Release 4
*/
public abstract class SecurityController {
/**
* Define and load a Java class.
* <p>
* In embeddings that care about security, the securityDomain
* must be associated with the defined class such that a call to
* <code>getSecurityDomain</code> with that class will return this security
* context.
* <p>
* @param name the name of the class
* @param data the bytecode of the class
* @param securityDomain some object specifying the security
* context of the code that is defining this class.
* Embeddings that don't care about security may allow
* null here. This value propagated from the values passed
* into methods of Context that evaluate scripts.
*/
public abstract Class defineClass(String name, byte[] data,
Object securityDomain);
/**
* Get dynamic security domain that allows an action only if it is allowed
* by the current Java stack and <i>securityDomain</i>. If
* <i>securityDomain</i> is null, return domain representing permissions
* allowed by the current stack.
*/
public abstract Object getDynamicSecurityDomain(Object securityDomain);
/**
* Call {@link Script#exec(Context cx, Scriptable scope)} of
* <i>script</i> under restricted security domain where an action is
* allowed only if it allowed according to the Java stack on the
* moment of the <i>execWithDomain</i> call and <i>securityDomain</i>.
* Any call to {@link #getDynamicSecurityDomain(Object)} during
* execution of {@link Script#exec(Context cx, Scriptable scope)}
* should return a domain incorporate restrictions imposed by
* <i>securityDomain</i>.
*/
public abstract Object execWithDomain(Context cx, Scriptable scope,
Script script, Object securityDomain)
throws JavaScriptException;
}

View File

@ -33,116 +33,16 @@
* file under either the NPL or the GPL.
*/
// API class
package org.mozilla.javascript;
/**
* This class describes the support needed to implement security.
* <p>
* Three main pieces of functionality are required to implement
* security for JavaScript. First, it must be possible to define
* classes with an associated security context. (This security
* context may be any object that has meaning to an embedding;
* for a client-side JavaScript embedding this would typically
* be an origin URL and/or a digital certificate.) Next it
* must be possible to get the current class context so that
* the implementation can determine securely which class is
* requesting a privileged action. And finally, it must be
* possible to map a class back into a security context so that
* additional classes may be defined with that security context.
* <p>
* These three pieces of functionality are encapsulated in the
* SecuritySupport class.
* <p>
* Additionally, an embedding may provide filtering on the
* Java classes that are visible to scripts through the
* <code>visibleToScripts</code> method.
*
* @see org.mozilla.javascript.Context
* @see java.lang.ClassLoader
* @since 1.4 Release 2
* @author Norris Boyd
*/
public interface SecuritySupport {
@deprecated Since Rhino 1.5 R4 this interface is split into {@link ClassShutter} interface with the single {@link ClassShutter#visibleToScripts(String fullClassName)} method and the abstract {@link SecurityController} class to implement domain-based security restrictions.
<p>
For binary compatibility with older code implementing SecuritySupport only to restrict class access via the visibleToScripts method, this interface extends {@link ClassShutter}, so to upgrade you code in this case simply replace SecuritySupport by ClassShutter and remove empty implementation of the defineClass, getClassContext and getSecurityDomain methods. Then call {@link Context#setClassShutter(ClassShutter)} in place of{@link Context#setSecuritySupport(SecuritySupport)}.
<p>
The new {@link SecurityController} is incompatible with the old security implementation, so if you previously had non-trivial implementation of the defineClass, getClassContext and getSecurityDomain methods, you need to upgrade you code to use the new {@link SecurityController}.
*/
public interface SecuritySupport extends ClassShutter {
/**
* Define and load a Java class.
* <p>
* In embeddings that care about security, the securityDomain
* must be associated with the defined class such that a call to
* <code>getSecurityDomain</code> with that class will return this security
* context.
* <p>
* @param name the name of the class
* @param data the bytecode of the class
* @param securityDomain some object specifying the security
* context of the code that is defining this class.
* Embeddings that don't care about security may allow
* null here. This value propagated from the values passed
* into methods of Context that evaluate scripts.
*/
public Class defineClass(String name, byte[] data,
Object securityDomain);
/**
* Get the current class Context.
* <p>
* This functionality is supplied by SecurityManager.getClassContext,
* but only one SecurityManager may be instantiated in a single JVM
* at any one time. So implementations that care about security must
* provide access to this functionality through this interface.
* <p>
* Note that the 0th entry of the returned array should be the class
* of the caller of this method. So if this method is implemented by
* calling SecurityManager.getClassContext, this method must allocate
* a new, shorter array to return.
*/
public Class[] getClassContext();
/**
* Return the security context associated with the given class.
* <p>
* If <code>cl</code> is a class defined through a call to
* SecuritySupport.defineClass, then return the security
* context from that call. Otherwise return null.
* @param cl a class potentially defined by defineClass
* @return a security context object previously passed to defineClass
*/
public Object getSecurityDomain(Class cl);
/**
* Return true iff the Java class with the given name should be exposed
* to scripts.
* <p>
* An embedding may filter which Java classes are exposed through
* LiveConnect to JavaScript scripts.
* <p>
* Due to the fact that there is no package reflection in Java,
* this method will also be called with package names. There
* is no way for Rhino to tell if "Packages.a.b" is a package name
* or a class that doesn't exist. What Rhino does is attempt
* to load each segment of "Packages.a.b.c": It first attempts to
* load class "a", then attempts to load class "a.b", then
* finally attempts to load class "a.b.c". On a Rhino installation
* without any SecuritySupport set, and without any of the
* above classes, the expression "Packages.a.b.c" will result in
* a [JavaPackage a.b.c] and not an error.
* <p>
* With SecuritySupport supplied, Rhino will first call
* visibleToScripts before attempting to look up the class name. If
* visibleToScripts returns false, the class name lookup is not
* performed and subsequent Rhino execution assumes the class is
* not present. So for "java.lang.System.out.println" the lookup
* of "java.lang.System" is skipped and thus Rhino assumes that
* "java.lang.System" doesn't exist. So then for "java.lang.System.out",
* Rhino attempts to load the class "java.lang.System.out" because
* it assumes that "java.lang.System" is a package name.
* <p>
* @param fullClassName the full name of the class (including the package
* name, with '.' as a delimiter). For example the
* standard string class is "java.lang.String"
* @return whether or not to reveal this class to scripts
*/
public boolean visibleToScripts(String fullClassName);
}

View File

@ -69,7 +69,7 @@ public class Codegen extends Interpreter {
public Object compile(Context cx, Scriptable scope, Node tree,
Object securityDomain,
SecuritySupport securitySupport,
SecurityController securityController,
ClassNameHelper nameHelper)
{
ObjArray classFiles = new ObjArray();
@ -78,7 +78,10 @@ public class Codegen extends Interpreter {
Exception e = null;
Class result = null;
DefiningClassLoader classLoader = new DefiningClassLoader();
DefiningClassLoader classLoader = null;
if (securityController == null) {
classLoader = new DefiningClassLoader();
}
nameHelper.reset();
try {
@ -97,12 +100,15 @@ public class Codegen extends Interpreter {
try {
if (repository.storeClass(name, classFile, isTopLevel)) {
Class clazz = null;
if (securitySupport != null) {
clazz = securitySupport.defineClass(name, classFile,
securityDomain);
}
if (clazz == null) {
Context.checkSecurityDomainRequired();
if (securityController != null) {
clazz = securityController.
defineClass(name, classFile, securityDomain);
if (clazz == null) {
throw new NullPointerException
("SecurityController.defineClass"
+" may not return null");
}
} else {
clazz = classLoader.defineClass(name, classFile);
ClassLoader loader = clazz.getClassLoader();
clazz = loader.loadClass(name);

View File

@ -1,36 +0,0 @@
#
# Default JavaScript security properties file.
#
# The contents of this file are subject to the Netscape Public
# License Version 1.1 (the "License"); you may not use this file
# except in compliance with the License. You may obtain a copy of
# the License at http://www.mozilla.org/NPL/
#
# Software distributed under the License is distributed on an "AS
# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
# implied. See the License for the specific language governing
# rights and limitations under the License.
#
# The Original Code is Rhino code, released
# May 6, 1998.
#
# The Initial Developer of the Original Code is Netscape
# Communications Corporation. Portions created by Netscape are
# Copyright (C) 1997-1999 Netscape Communications Corporation. All
# Rights Reserved.
#
# Contributor(s):
# Norris Boyd
#
# Alternatively, the contents of this file may be used under the
# terms of the GNU Public License (the "GPL"), in which case the
# provisions of the GPL are applicable instead of those above.
# If you wish to allow use of your version of this file only
# under the terms of the GPL and not to allow others to use your
# version of this file under the NPL, indicate your decision by
# deleting the provisions above and replace them with the notice
# and other provisions required by the GPL. If you do not delete
# the provisions above, a recipient may use your version of this
# file under either the NPL or the GPL.
security.requireSecurityDomain = false

View File

@ -397,7 +397,7 @@ class Runner implements Runnable {
}
public void run() {
Context cx = Context.enter();
Context cx = Main.enterContext();
try {
if (f != null)

View File

@ -0,0 +1,222 @@
/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* The contents of this file are subject to the Netscape Public
* License Version 1.1 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.mozilla.org/NPL/
*
* Software distributed under the License is distributed on an "AS
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express oqr
* implied. See the License for the specific language governing
* rights and limitations under the License.
*
* The Original Code is Rhino code, released
* May 6, 1999.
*
* The Initial Developer of the Original Code is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1997-1999 Netscape Communications Corporation. All
* Rights Reserved.
*
* Contributor(s):
* Igor Bukanov
*
* Alternatively, the contents of this file may be used under the
* terms of the GNU Public License (the "GPL"), in which case the
* provisions of the GPL are applicable instead of those above.
* If you wish to allow use of your version of this file only
* under the terms of the GPL and not to allow others to use your
* version of this file under the NPL, indicate your decision by
* deleting the provisions above and replace them with the notice
* and other provisions required by the GPL. If you do not delete
* the provisions above, a recipient may use your version of this
* file under either the NPL or the GPL.
*/
package org.mozilla.javascript.tools.shell;
import java.security.*;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Hashtable;
import java.util.Enumeration;
import org.mozilla.javascript.*;
import org.mozilla.classfile.*;
public class JavaPolicySecurity extends SecurityProxy
{
private static class Loader extends ClassLoader
{
Class defineClass(String name, byte[] data, ProtectionDomain domain) {
return super.defineClass(name, data, 0, data.length, domain);
}
}
private static class ContextPermissions extends PermissionCollection
{
// Construct PermissionCollection that permits an action only
// if it is permitted by staticDomain and by security context of Java stack on
// the moment of constructor invocation
ContextPermissions(ProtectionDomain staticDomain) {
_context = AccessController.getContext();
if (staticDomain != null) {
_statisPermissions = staticDomain.getPermissions();
}
setReadOnly();
}
public void add(Permission permission) {
throw new RuntimeException("NOT IMPLEMENTED");
}
public boolean implies(Permission permission) {
if (_statisPermissions != null) {
if (!_statisPermissions.implies(permission)) {
return false;
}
}
try {
_context.checkPermission(permission);
return true;
}catch (AccessControlException ex) {
return false;
}
}
public Enumeration elements()
{
return new Enumeration() {
public boolean hasMoreElements() { return false; }
public Object nextElement() { return null; }
};
}
public String toString() {
StringBuffer sb = new StringBuffer();
sb.append(getClass().getName());
sb.append('@');
sb.append(Integer.toHexString(System.identityHashCode(this)));
sb.append(" (context=");
sb.append(_context);
sb.append(", static_permitions=");
sb.append(_statisPermissions);
sb.append(')');
return sb.toString();
}
AccessControlContext _context;
PermissionCollection _statisPermissions;
}
public JavaPolicySecurity() {
// To trigger error on jdk-1.1 with lazy load
new CodeSource(null, null);
}
protected void callProcessFileSecure(final Context cx,
final Scriptable scope,
final String filename)
{
AccessController.doPrivileged(new PrivilegedAction() {
public Object run() {
URL url = getUrlObj(filename);
ProtectionDomain staticDomain = getUrlDomain(url);
Main.processFileSecure(cx, scope, url.toExternalForm(),
staticDomain);
return null;
}
});
}
private URL getUrlObj(String url) {
URL urlObj;
try {
urlObj = new URL(url);
}catch (MalformedURLException ex) {
// Assume as Main.processFileSecure it is file, need to build its
// URL
String curDir = System.getProperty("user.dir");
curDir = curDir.replace('\\', '/');
if (!curDir.endsWith("/")) {
curDir = curDir+'/';
}
try {
URL curDirURL = new URL("file:"+curDir);
urlObj = new URL(curDirURL, url);
}catch (MalformedURLException ex2) {
throw new RuntimeException
("Can not construct file URL for '"+url+"':"
+ex2.getMessage());
}
}
return urlObj;
}
private ProtectionDomain getUrlDomain(URL url) {
CodeSource cs = new CodeSource(url, null);
PermissionCollection pc = Policy.getPolicy().getPermissions(cs);
return new ProtectionDomain(cs, pc);
}
void doPrivileged(final Runnable code) {
}
public Class defineClass(String name, byte[] data,
Object securityDomain)
{
ProtectionDomain domain = (ProtectionDomain)securityDomain;
Loader loader = new Loader();
return loader.defineClass(name, data, domain);
}
public Object getDynamicSecurityDomain(Object securityDomain)
{
ProtectionDomain staticDomain = (ProtectionDomain)securityDomain;
return getDynamicDomain(staticDomain);
}
private ProtectionDomain getDynamicDomain(ProtectionDomain staticDomain) {
ContextPermissions p = new ContextPermissions(staticDomain);
ProtectionDomain contextDomain = new ProtectionDomain(null, p);
return contextDomain;
}
public Object execWithDomain(final Context cx, final Scriptable scope,
final Script script, Object securityDomain)
throws JavaScriptException
{
ProtectionDomain staticDomain = (ProtectionDomain)securityDomain;
// There is no direct way in Java to intersect permitions according
// stack context with additional domain.
// The following implementation first constructs ProtectionDomain
// that allows actions only allowed by both staticDomain and current
// stack context, and then constructs AccessController for this dynamic
// domain.
// If this is too slow, alternative solution would be to generate
// class per domain with a proxy method to call to infect
// java stack.
// Another optimization in case of scripts coming from "world" domain,
// that is having minimal default privileges is to construct
// one AccessControlContext based on ProtectionDomain
// with least possible privileges and simply call
// AccessController.doPrivileged with this untrusted context
ProtectionDomain dynamicDomain = getDynamicDomain(staticDomain);
ProtectionDomain[] tmp = { dynamicDomain };
AccessControlContext restricted = new AccessControlContext(tmp);
PrivilegedExceptionAction action = new PrivilegedExceptionAction() {
public Object run() throws JavaScriptException {
return script.exec(cx, scope);
}
};
try {
return AccessController.doPrivileged(action, restricted);
}catch (PrivilegedActionException ex) {
throw (JavaScriptException)(ex.getException());
}
}
}

View File

@ -65,6 +65,14 @@ public class Main {
* execute scripts.
*/
public static void main(String args[]) {
try {
if (Boolean.getBoolean("rhino.use_java_policy_security")) {
initJavaPolicySecuritySupport();
}
}catch (SecurityException ex) {
ex.printStackTrace(System.err);
}
int result = exec(args);
if (result != 0)
System.exit(result);
@ -74,7 +82,7 @@ public class Main {
* Execute the given arguments, but don't System.exit at the end.
*/
public static int exec(String args[]) {
Context cx = Context.enter();
Context cx = enterContext();
// Create the top-level scope object.
global = getGlobal();
errorReporter = new ToolErrorReporter(false, global.getErr());
@ -102,7 +110,7 @@ public class Main {
public static Global getGlobal() {
if (global == null) {
try {
global = new Global(Context.enter());
global = new Global(enterContext());
}
finally {
Context.exit();
@ -111,6 +119,14 @@ public class Main {
return global;
}
static Context enterContext() {
Context cx = new Context();
if (securityImpl != null) {
cx.setSecurityController(securityImpl);
}
return Context.enter(cx);
}
/**
* Parse arguments.
*/
@ -147,7 +163,7 @@ public class Main {
if (++i == args.length)
usage(arg);
Reader reader = new StringReader(args[i]);
evaluateReader(cx, global, reader, "<command>", 1);
evaluateReader(cx, global, reader, "<command>", 1, null);
continue;
}
if (arg.equals("-w")) {
@ -174,6 +190,25 @@ public class Main {
System.exit(1);
}
private static void initJavaPolicySecuritySupport() {
Throwable exObj;
try {
Class cl = Class.forName
("org.mozilla.javascript.tools.shell.JavaPolicySecurity");
securityImpl = (SecurityProxy)cl.newInstance();
return;
}catch (ClassNotFoundException ex) {
exObj = ex;
}catch (IllegalAccessException ex) {
exObj = ex;
}catch (InstantiationException ex) {
exObj = ex;
}catch (LinkageError ex) {
exObj = ex;
}
throw new RuntimeException("Can not load security support: "+exObj);
}
/**
* Evaluate JavaScript source.
*
@ -223,7 +258,7 @@ public class Main {
}
Reader reader = new StringReader(source);
Object result = evaluateReader(cx, global, reader,
"<stdin>", startline);
"<stdin>", startline, null);
if (result != cx.getUndefinedValue()) {
try {
global.getErr().println(cx.toString(result));
@ -251,6 +286,16 @@ public class Main {
public static void processFile(Context cx, Scriptable scope,
String filename)
{
if (securityImpl == null) {
processFileSecure(cx, scope, filename, null);
}else {
securityImpl.callProcessFileSecure(cx, scope, filename);
}
}
static void processFileSecure(Context cx, Scriptable scope,
String filename, Object securityDomain)
{
Reader in = null;
// Try filename first as URL
@ -306,16 +351,17 @@ public class Main {
// Here we evalute the entire contents of the file as
// a script. Text is printed only if the print() function
// is called.
evaluateReader(cx, scope, in, filename, 1);
evaluateReader(cx, scope, in, filename, 1, securityDomain);
}
public static Object evaluateReader(Context cx, Scriptable scope,
Reader in, String sourceName,
int lineno)
int lineno, Object securityDomain)
{
Object result = cx.getUndefinedValue();
try {
result = cx.evaluateReader(scope, in, sourceName, lineno, null);
result = cx.evaluateReader(scope, in, sourceName, lineno,
securityDomain);
}
catch (WrappedException we) {
global.getErr().println(we.getWrappedException().toString());
@ -402,4 +448,5 @@ public class Main {
//static private DebugShell debugShell;
static boolean processStdin = true;
static Vector fileList = new Vector(5);
private static SecurityProxy securityImpl;
}

View File

@ -0,0 +1,45 @@
/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* The contents of this file are subject to the Netscape Public
* License Version 1.1 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.mozilla.org/NPL/
*
* Software distributed under the License is distributed on an "AS
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express oqr
* implied. See the License for the specific language governing
* rights and limitations under the License.
*
* The Original Code is Rhino code, released
* May 6, 1999.
*
* The Initial Developer of the Original Code is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1997-1999 Netscape Communications Corporation. All
* Rights Reserved.
*
* Contributor(s):
* Igor Bukanov
*
* Alternatively, the contents of this file may be used under the
* terms of the GNU Public License (the "GPL"), in which case the
* provisions of the GPL are applicable instead of those above.
* If you wish to allow use of your version of this file only
* under the terms of the GPL and not to allow others to use your
* version of this file under the NPL, indicate your decision by
* deleting the provisions above and replace them with the notice
* and other provisions required by the GPL. If you do not delete
* the provisions above, a recipient may use your version of this
* file under either the NPL or the GPL.
*/
package org.mozilla.javascript.tools.shell;
import org.mozilla.javascript.*;
public abstract class SecurityProxy extends SecurityController
{
protected abstract void callProcessFileSecure(Context cx, Scriptable scope,
String filename);
}