001: /* ***** BEGIN LICENSE BLOCK *****
002: * Version: MPL 1.1/GPL 2.0
003: *
004: * The contents of this file are subject to the Mozilla Public License Version
005: * 1.1 (the "License"); you may not use this file except in compliance with
006: * the License. You may obtain a copy of the License at
007: * http://www.mozilla.org/MPL/
008: *
009: * Software distributed under the License is distributed on an "AS IS" basis,
010: * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
011: * for the specific language governing rights and limitations under the
012: * License.
013: *
014: * The Original Code is Rhino code, released May 6, 1999.
015: *
016: * The Initial Developer of the Original Code is
017: * Netscape Communications Corporation.
018: * Portions created by the Initial Developer are Copyright (C) 1997-1999
019: * the Initial Developer. All Rights Reserved.
020: *
021: * Contributor(s):
022: *
023: * Alternatively, the contents of this file may be used under the terms of
024: * the GNU General Public License Version 2 or later (the "GPL"), in which
025: * case the provisions of the GPL are applicable instead of those above. If
026: * you wish to allow use of your version of this file only under the terms of
027: * the GPL and not to allow others to use your version of this file under the
028: * MPL, indicate your decision by deleting the provisions above and replacing
029: * them with the notice and other provisions required by the GPL. If you do
030: * not delete the provisions above, a recipient may use your version of this
031: * file under either the MPL or the GPL.
032: *
033: * ***** END LICENSE BLOCK ***** */
034:
035: package org.mozilla.javascript;
036:
037: import java.lang.ref.SoftReference;
038: import java.lang.reflect.UndeclaredThrowableException;
039: import java.security.AccessController;
040: import java.security.CodeSource;
041: import java.security.Policy;
042: import java.security.PrivilegedAction;
043: import java.security.PrivilegedActionException;
044: import java.security.PrivilegedExceptionAction;
045: import java.security.SecureClassLoader;
046: import java.util.Map;
047: import java.util.WeakHashMap;
048:
049: import org.mozilla.classfile.ByteCode;
050: import org.mozilla.classfile.ClassFileWriter;
051:
052: /**
053: * A security controller relying on Java {@link Policy} in effect. When you use
054: * this security controller, your securityDomain objects must be instances of
055: * {@link CodeSource} representing the location from where you load your
056: * scripts. Any Java policy "grant" statements matching the URL and certificate
057: * in code sources will apply to the scripts. If you specify any certificates
058: * within your {@link CodeSource} objects, it is your responsibility to verify
059: * (or not) that the script source files are signed in whatever
060: * implementation-specific way you're using.
061: * @author Attila Szegedi
062: */
063: public class PolicySecurityController extends SecurityController {
064: private static final byte[] secureCallerImplBytecode = loadBytecode();
065:
066: // We're storing a CodeSource -> (ClassLoader -> SecureRenderer), since we
067: // need to have one renderer per class loader. We're using weak hash maps
068: // and soft references all the way, since we don't want to interfere with
069: // cleanup of either CodeSource or ClassLoader objects.
070: private static final Map callers = new WeakHashMap();
071:
072: public Class getStaticSecurityDomainClassInternal() {
073: return CodeSource.class;
074: }
075:
076: private static class Loader extends SecureClassLoader implements
077: GeneratedClassLoader {
078: private final CodeSource codeSource;
079:
080: Loader(ClassLoader parent, CodeSource codeSource) {
081: super (parent);
082: this .codeSource = codeSource;
083: }
084:
085: public Class defineClass(String name, byte[] data) {
086: return defineClass(name, data, 0, data.length, codeSource);
087: }
088:
089: public void linkClass(Class cl) {
090: resolveClass(cl);
091: }
092: }
093:
094: public GeneratedClassLoader createClassLoader(
095: final ClassLoader parent, final Object securityDomain) {
096: return (Loader) AccessController
097: .doPrivileged(new PrivilegedAction() {
098: public Object run() {
099: return new Loader(parent,
100: (CodeSource) securityDomain);
101: }
102: });
103: }
104:
105: public Object getDynamicSecurityDomain(Object securityDomain) {
106: // No separate notion of dynamic security domain - just return what was
107: // passed in.
108: return securityDomain;
109: }
110:
111: public Object callWithDomain(final Object securityDomain,
112: final Context cx, Callable callable, Scriptable scope,
113: Scriptable this Obj, Object[] args) {
114: // Run in doPrivileged as we might be checked for "getClassLoader"
115: // runtime permission
116: final ClassLoader classLoader = (ClassLoader) AccessController
117: .doPrivileged(new PrivilegedAction() {
118: public Object run() {
119: return cx.getApplicationClassLoader();
120: }
121: });
122: final CodeSource codeSource = (CodeSource) securityDomain;
123: Map classLoaderMap;
124: synchronized (callers) {
125: classLoaderMap = (Map) callers.get(codeSource);
126: if (classLoaderMap == null) {
127: classLoaderMap = new WeakHashMap();
128: callers.put(codeSource, classLoaderMap);
129: }
130: }
131: SecureCaller caller;
132: synchronized (classLoaderMap) {
133: SoftReference ref = (SoftReference) classLoaderMap
134: .get(classLoader);
135: if (ref != null) {
136: caller = (SecureCaller) ref.get();
137: } else {
138: caller = null;
139: }
140: if (caller == null) {
141: try {
142: // Run in doPrivileged as we'll be checked for
143: // "createClassLoader" runtime permission
144: caller = (SecureCaller) AccessController
145: .doPrivileged(new PrivilegedExceptionAction() {
146: public Object run() throws Exception {
147: Loader loader = new Loader(
148: classLoader, codeSource);
149: Class c = loader.defineClass(
150: SecureCaller.class
151: .getName()
152: + "Impl",
153: secureCallerImplBytecode);
154: return c.newInstance();
155: }
156: });
157: classLoaderMap.put(classLoader, new SoftReference(
158: caller));
159: } catch (PrivilegedActionException ex) {
160: throw new UndeclaredThrowableException(ex
161: .getCause());
162: }
163: }
164: }
165: return caller.call(callable, cx, scope, this Obj, args);
166: }
167:
168: public abstract static class SecureCaller {
169: public abstract Object call(Callable callable, Context cx,
170: Scriptable scope, Scriptable this Obj, Object[] args);
171: }
172:
173: private static byte[] loadBytecode() {
174: String secureCallerClassName = SecureCaller.class.getName();
175: ClassFileWriter cfw = new ClassFileWriter(secureCallerClassName
176: + "Impl", secureCallerClassName, "<generated>");
177: cfw.startMethod("<init>", "()V", ClassFileWriter.ACC_PUBLIC);
178: cfw.addALoad(0);
179: cfw.addInvoke(ByteCode.INVOKESPECIAL, secureCallerClassName,
180: "<init>", "()V");
181: cfw.add(ByteCode.RETURN);
182: cfw.stopMethod((short) 1);
183: String callableCallSig = "Lorg/mozilla/javascript/Context;"
184: + "Lorg/mozilla/javascript/Scriptable;"
185: + "Lorg/mozilla/javascript/Scriptable;"
186: + "[Ljava/lang/Object;)Ljava/lang/Object;";
187:
188: cfw
189: .startMethod(
190: "call",
191: "(Lorg/mozilla/javascript/Callable;"
192: + callableCallSig,
193: (short) (ClassFileWriter.ACC_PUBLIC | ClassFileWriter.ACC_FINAL));
194: for (int i = 1; i < 6; ++i) {
195: cfw.addALoad(i);
196: }
197: cfw.addInvoke(ByteCode.INVOKEINTERFACE,
198: "org/mozilla/javascript/Callable", "call", "("
199: + callableCallSig);
200: cfw.add(ByteCode.ARETURN);
201: cfw.stopMethod((short) 6);
202: return cfw.toByteArray();
203: }
204: }
|