/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0 * * The contents of this file are subject to the Mozilla 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/MPL/ * * 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 the Initial Developer are Copyright (C) 1997-1999 * the Initial Developer. All Rights Reserved. * * Contributor(s): * Igor Bukanov * * Alternatively, the contents of this file may be used under the terms of * the GNU General Public License Version 2 or later (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 * MPL, indicate your decision by deleting the provisions above and replacing * 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 MPL or the GPL. * * ***** END LICENSE BLOCK ***** */ package org.mozilla.javascript.tools.shell; import java.security.*; import java.net.MalformedURLException; import java.net.URL; import java.util.Enumeration; import org.mozilla.javascript.*; public class JavaPolicySecurity extends SecurityProxy { public Class getStaticSecurityDomainClassInternal() { return ProtectionDomain.class; } private static class Loader extends ClassLoader implements GeneratedClassLoader { private ProtectionDomain domain; Loader(ClassLoader parent, ProtectionDomain domain) { super(parent != null ? parent : getSystemClassLoader()); this.domain = domain; } public Class defineClass(String name, byte[] data) { return super.defineClass(name, data, 0, data.length, domain); } public void linkClass(Class cl) { resolveClass(cl); } } private static class ContextPermissions extends PermissionCollection { static final long serialVersionUID = -1721494496320750721L; // 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, (java.security.cert.Certificate[])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; cs = new CodeSource(url, (java.security.cert.Certificate[])null); PermissionCollection pc = Policy.getPolicy().getPermissions(cs); return new ProtectionDomain(cs, pc); } public GeneratedClassLoader createClassLoader(ClassLoader parentLoader, Object securityDomain) { ProtectionDomain domain = (ProtectionDomain)securityDomain; return new Loader(parentLoader, 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 callWithDomain(Object securityDomain, final Context cx, final Callable callable, final Scriptable scope, final Scriptable thisObj, final Object[] args) { 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); PrivilegedAction action = new PrivilegedAction() { public Object run() { return callable.call(cx, scope, thisObj, args); } }; return AccessController.doPrivileged(action, restricted); } }