/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.sun.jini.start; import com.sun.jini.collection.WeakIdentityMap; import net.jini.security.policy.DynamicPolicy; import java.security.AccessController; import java.security.AllPermission; import java.security.CodeSource; import java.security.Permission; import java.security.Permissions; import java.security.PermissionCollection; import java.security.Policy; import java.security.Principal; import java.security.PrivilegedAction; import java.security.ProtectionDomain; /** * Security policy provider which handles permission queries and grants by * delegating to different policy providers depending on the class loader * involved. Each <code>LoaderSplitPolicyProvider</code> instance wraps two * underlying policy providers: * <ul> * <li>a class-loader specific policy provider, consulted for permission * queries/grants pertaining to that class loader, any child class loaders that * delegate to it, or the <code>null</code> class loader, and * <li>a default policy provider, consulted for all other operations (aside * from {@link #refresh}, which applies to both policies). * </ul> * * @author Sun Microsystems, Inc. * * @since 2.0 */ public class LoaderSplitPolicyProvider extends Policy implements DynamicPolicy { private static final ProtectionDomain myDomain = (ProtectionDomain) AccessController.doPrivileged(new PrivilegedAction() { public Object run() { return LoaderSplitPolicyProvider.class.getProtectionDomain(); } }); private final ClassLoader loader; private final Policy loaderPolicy; private final Policy defaultPolicy; private final WeakIdentityMap delegateMap = new WeakIdentityMap(); /** * Creates a new <code>LoaderSplitPolicyProvider</code> instance which * delegates to <code>loaderPolicy</code> any permission query/grant * operations involving protection domains or classes with the given class * loader, any child class loader of the given class loader, or the * <code>null</code> class loader; all other operations are delegated to * <code>defaultPolicy</code> (with the exception of <code>refresh</code>, * which applies to both policies). * * @param loader class loader for which associated permission query/grant * operations should be forwarded to <code>loaderPolicy</code> * @param loaderPolicy class loader-specific security policy provider * @param defaultPolicy default security policy provider * @throws NullPointerException if <code>loader</code>, * <code>loaderPolicy</code> or <code>defaultPolicy</code> is * <code>null</code> */ public LoaderSplitPolicyProvider(ClassLoader loader, Policy loaderPolicy, Policy defaultPolicy) { if (loader == null || loaderPolicy == null || defaultPolicy == null) { throw new NullPointerException(); } this.loader = loader; this.loaderPolicy = loaderPolicy; this.defaultPolicy = defaultPolicy; ensureDependenciesResolved(); } /** * Delegates to the corresponding <code>getPermissions</code> method of the * underlying default policy. * * @param source code source for which to look up permissions * @return set of permissions allowed for the given code source */ public PermissionCollection getPermissions(CodeSource source) { return defaultPolicy.getPermissions(source); } /** * If the given protection domain is the protection domain of this class, * then a newly-created <code>PermissionCollection</code> containing {@link * AllPermission} is returned. If not, delegates to the corresponding * <code>getPermissions</code> method of the underlying policy associated * with the loader of the given class (the loader-specific policy if the * class loader is <code>null</code>, the same as or a child of the loader * specified in the constructor for this instance, or the default loader * otherwise). * * @param domain protection domain for which to look up permissions * @return set of permissions allowed for given protection domain */ public PermissionCollection getPermissions(ProtectionDomain domain) { if (domain == myDomain) { PermissionCollection pc = new Permissions(); pc.add(new AllPermission()); return pc; } else { return getDelegate(domain.getClassLoader()).getPermissions(domain); } } /** * If the given protection domain is the protection domain of this class, * then <code>true</code> is returned. If not, delegates to the * <code>implies</code> method of the underlying policy associated with the * loader of the given class (the loader-specific policy if the class * loader is <code>null</code>, the same as or a child of the loader * specified in the constructor for this instance, or the default loader * otherwise). * * @param domain protection domain in which to check implication * @param permission permission to test implication of * @return <code>true</code> if permission is implied by permissions of * given protection domain, <code>false</code> otherwise */ public boolean implies(ProtectionDomain domain, Permission permission) { return domain == myDomain || getDelegate(domain.getClassLoader()).implies(domain, permission); } /** * Invokes <code>refresh</code> on both the loader-specific and default * underlying policy providers. */ public void refresh() { loaderPolicy.refresh(); defaultPolicy.refresh(); } /** * Returns <code>true</code> if both of the underlying policy providers * implement {@link DynamicPolicy} and return <code>true</code> from calls * to <code>grantSupported</code>; returns <code>false</code> otherwise. * * @return {@inheritDoc} */ public boolean grantSupported() { return loaderPolicy instanceof DynamicPolicy && ((DynamicPolicy) loaderPolicy).grantSupported() && defaultPolicy instanceof DynamicPolicy && ((DynamicPolicy) defaultPolicy).grantSupported(); } /** * If both underlying policy providers support dynamic grants, delegates to * the <code>grant</code> method of the underlying policy associated with * the loader of the given class (the loader-specific policy if the class * loader is <code>null</code>, the same as or a child of the loader * specified in the constructor for this instance, or the default loader * otherwise). If at least one of the underlying policy providers does not * support dynamic grants, throws an * <code>UnsupportedOperationException</code>. * * @param cl {@inheritDoc} * @param principals {@inheritDoc} * @param permissions {@inheritDoc} * @throws UnsupportedOperationException {@inheritDoc} * @throws SecurityException {@inheritDoc} * @throws NullPointerException {@inheritDoc} */ public void grant(Class cl, Principal[] principals, Permission[] permissions) { if (!grantSupported()) { throw new UnsupportedOperationException("grants not supported"); } ((DynamicPolicy) getDelegate(getClassLoader(cl))).grant( cl, principals, permissions); } /** * If both underlying policy providers support dynamic grants, delegates to * the <code>getGrants</code> method of the underlying policy associated * with the loader of the given class (the loader-specific policy if the * class loader is <code>null</code>, the same as or a child of the loader * specified in the constructor for this instance, or the default loader * otherwise). If at least one of the underlying policy providers does not * support dynamic grants, throws an * <code>UnsupportedOperationException</code>. * * @param cl {@inheritDoc} * @param principals {@inheritDoc} * @return {@inheritDoc} * @throws UnsupportedOperationException {@inheritDoc} * @throws NullPointerException {@inheritDoc} */ public Permission[] getGrants(Class cl, Principal[] principals) { if (!grantSupported()) { throw new UnsupportedOperationException("grants not supported"); } return ((DynamicPolicy) getDelegate(getClassLoader(cl))).getGrants( cl, principals); } /** * Ensures that any classes depended on by this policy provider are * resolved. This is to preclude lazy resolution of such classes during * operation of the provider, which can result in deadlock as described by * bug 4911907. */ private void ensureDependenciesResolved() { // force class resolution by pre-invoking method called by implies() getDelegate(loader); } private Policy getDelegate(final ClassLoader ldr) { if (ldr == null) { /* This special case is needed so that implies queries from the * BasicInvocationDispatcher.checkClientPermission method * will be handled by the loader-specific (i.e., service) policy. */ return loaderPolicy; } Policy p; synchronized (delegateMap) { p = (Policy) delegateMap.get(ldr); } if (p == null) { p = (Policy) AccessController.doPrivileged(new PrivilegedAction() { public Object run() { for (ClassLoader l = ldr; l != null; l = l.getParent()) { if (l == loader) { return loaderPolicy; } } return defaultPolicy; } }); synchronized (delegateMap) { delegateMap.put(ldr, p); } } return p; } private static ClassLoader getClassLoader(final Class cl) { return (ClassLoader) AccessController.doPrivileged( new PrivilegedAction() { public Object run() { return cl.getClassLoader(); } }); } }