Author: peter_firmstone Date: Fri Nov 2 10:28:22 2012 New Revision: 1404911
URL: http://svn.apache.org/viewvc?rev=1404911&view=rev Log: RemotePolicy refactoring, added @Beta status. Modified: river/jtsk/trunk/src/org/apache/river/api/security/AbstractPolicy.java river/jtsk/trunk/src/org/apache/river/api/security/RemotePolicy.java river/jtsk/trunk/src/org/apache/river/api/security/RemotePolicyProvider.java Modified: river/jtsk/trunk/src/org/apache/river/api/security/AbstractPolicy.java URL: http://svn.apache.org/viewvc/river/jtsk/trunk/src/org/apache/river/api/security/AbstractPolicy.java?rev=1404911&r1=1404910&r2=1404911&view=diff ============================================================================== --- river/jtsk/trunk/src/org/apache/river/api/security/AbstractPolicy.java (original) +++ river/jtsk/trunk/src/org/apache/river/api/security/AbstractPolicy.java Fri Nov 2 10:28:22 2012 @@ -38,12 +38,14 @@ import java.util.TreeSet; import net.jini.security.GrantPermission; import net.jini.security.policy.UmbrellaGrantPermission; import java.util.concurrent.ConcurrentHashMap; +import org.apache.river.api.common.Beta; /** * A common superclass with utility methods for policy providers. * * */ +@Beta public abstract class AbstractPolicy extends Policy { protected final Permission umbrella = new UmbrellaGrantPermission(); protected final Permission ALL_PERMISSION = new AllPermission(); @@ -68,12 +70,16 @@ public abstract class AbstractPolicy ext Iterator<PermissionGrant> grantsItr = grants.iterator(); while (grantsItr.hasNext()) { PermissionGrant grant = grantsItr.next(); + checkCallerHasGrants(grant); + } + } + + protected final void checkCallerHasGrants(PermissionGrant grant) throws SecurityException { Collection<Permission> permCol = grant.getPermissions(); Permission[] perms = permCol.toArray(new Permission[permCol.size()]); checkNullElements(perms); Guard g = new GrantPermission(perms); g.checkGuard(this); - } } /** Modified: river/jtsk/trunk/src/org/apache/river/api/security/RemotePolicy.java URL: http://svn.apache.org/viewvc/river/jtsk/trunk/src/org/apache/river/api/security/RemotePolicy.java?rev=1404911&r1=1404910&r2=1404911&view=diff ============================================================================== --- river/jtsk/trunk/src/org/apache/river/api/security/RemotePolicy.java (original) +++ river/jtsk/trunk/src/org/apache/river/api/security/RemotePolicy.java Fri Nov 2 10:28:22 2012 @@ -21,14 +21,12 @@ package org.apache.river.api.security; import java.io.IOException; import net.jini.security.GrantPermission; import net.jini.security.policy.UmbrellaGrantPermission; +import org.apache.river.api.common.Beta; /** * <p> * RemotePolicy is a service api that can be implemented by a distributed Policy service, * allowing local Policy providers to be updated remotely by a djinn group administrator. - * </p><p> - * No service implementation has been provided, DynamicPolicyProvider does - * implement this interface to simplify creation of such a service. * </p> * <h2>Notes for implementors:</h2> * <p> @@ -36,8 +34,8 @@ import net.jini.security.policy.Umbrella * require client and server authentication, in addition the proxy must be a * reflective proxy only, as DownloadPermission should not be granted, which is * also beneficial to reduced network load at the administrator client. - * RemotePolicy may be submitted to a lookup service, where an administrator - * client will look it up and replace PermissionGrant's periodically. + * RemotePolicy may be submitted to a lookup service, where a group administrator + * will replace PermissionGrant's periodically. * </p><p> * To reduce network load, the administrator client may delay updates by * lazily processing updates in a serial manner. New RemotePolicy services @@ -55,13 +53,12 @@ import net.jini.security.policy.Umbrella * </p><p> * In addition, replicating administrator clients may register a pseudo RemotePolicy * in order to track the primary administrator client and take over in the - * event it fails. Failure may be failure to authenticate or failure to renew - * a Lease. + * event it fails. Failure may be failure to authenticate or Lease expiry. * </p><p> - * RemotePolicy, if it encapsulates an underlying RemotePolicy, does not + * RemotePolicy, if it encapsulates another nested RemotePolicy, does not * delegate updates to the base RemotePolicy, this is in case an * implementer wants a number of different layers of RemotePolicy, where - * each layer represents a different administrator role or responsibility. + * each layer represents a different administrator group role or responsibility. * The administrator's subject must hold the necessary permissions in order * to grant them, including GrantPermission and PolicyPermission("REMOTE"). * </p><p> @@ -80,6 +77,9 @@ import net.jini.security.policy.Umbrella * </p><p> * DefaultPolicyParser has been provided for an administrator client to * parse standard java format policy file's, to create PermissionGrant's. + * </p><p> + * If a node participates in more than one djinn group and registers with more + * than one lookup service, RemotePolicy's may be nested. * </p> * * @since 2.2.1 @@ -90,24 +90,51 @@ import net.jini.security.policy.Umbrella * @see DefaultPolicyScanner * @see PolicyPermission */ +@Beta public interface RemotePolicy { /** * Replaces the existing RemotePolicy's PermissionGrant's. * * The array is defensively copied, the caller, must have - * RuntimePermission("getProtectionDomain") + * RuntimePermission("getProtectionDomain") and PolicyPermission("Remote"), * as well as GrantPermission or UmbrellaGrantPermission for every * Permission granted by each PermissionGrant. * * If the calling Subject doesn't have sufficient permission, the - * first permission that fails will include the SecurityException as the - * cause of the thrown IOException. + * first permission that fails will be logged locally and the PermissionGrant + * will not be included in the policy update. SecurityException's are + * logged as level WARNING, NullPointerException as SEVERE. + * + * No security policy information will be returned directly or by way of exception + * to avoid providing an attacker with information that could lead to + * privilege escalation. * * Permissions required by the callers Subject should be set in the - * local policy files at the RemotePolicy server. + * local policy files at the RemotePolicy service server. + * + * Where an IOException is thrown, it should be assumed no update to the + * RemotePolicy has occurred. The policy is idempotent and the update may + * be retried. + * + * PermissionGrant's included in the policy will be the intersection + * of the Set of PermissionGrant's delivered by the caller and the + * those authorised by the local policy. No attempt should + * be made by the RemotePolicy implementation to grant a subset of Permissions + * contained in a single PermissionGrant, each individual PermissionGrant should be + * either allowed or denied atomically. + * + * The djinn group administrator needn't be concerned if the RemotePolicy + * node doesn't accept all grants, it is up to the node administrator participating + * in the djinn to determine trust. + * + * Administrators should group Permissions into PermissionGrant's based + * on component functionality, if any of the Permissions are not allowed + * then none of the permissions required for functionality of that component + * or service will be granted, this is preferred to partial functionality, + * which is harder to debug. * - * Where an IOException is thrown, no update to the - * RemotePolicy has occurred. + * Each node participating in a djinn may have up to one RemotePolicy + * service per group. * * @param policyPermissions * @throws java.io.IOException Modified: river/jtsk/trunk/src/org/apache/river/api/security/RemotePolicyProvider.java URL: http://svn.apache.org/viewvc/river/jtsk/trunk/src/org/apache/river/api/security/RemotePolicyProvider.java?rev=1404911&r1=1404910&r2=1404911&view=diff ============================================================================== --- river/jtsk/trunk/src/org/apache/river/api/security/RemotePolicyProvider.java (original) +++ river/jtsk/trunk/src/org/apache/river/api/security/RemotePolicyProvider.java Fri Nov 2 10:28:22 2012 @@ -29,6 +29,7 @@ import java.security.PermissionCollectio import java.security.Policy; import java.security.PrivilegedAction; import java.security.ProtectionDomain; +import java.security.Security; import java.security.UnresolvedPermission; import java.util.ArrayList; import java.util.Arrays; @@ -45,15 +46,22 @@ import java.util.TreeSet; import java.util.logging.Level; import java.util.logging.Logger; import net.jini.security.GrantPermission; +import net.jini.security.policy.PolicyInitializationException; +import org.apache.river.api.common.Beta; /** - * + * RemotePolicy provider implementation. * */ +@Beta public class RemotePolicyProvider extends AbstractPolicy implements RemotePolicy, ScalableNestedPolicy{ + private static final String basePolicyClassProperty = + "org.apache.river.api.security.RemotePolicyProvider.basePolicyClass"; + private static final String defaultBasePolicyClass = + "org.apache.river.api.security.ConcurrentPolicyFile"; - private static final Logger logger = Logger.getLogger("net.jini.security.policy"); + private static final Logger logger = Logger.getLogger("org.apache.river.api.security"); private static final ProtectionDomain policyDomain = AccessController.doPrivileged(new PrivilegedAction<ProtectionDomain>(){ @@ -81,10 +89,40 @@ public class RemotePolicyProvider extend private final boolean basePolicyIsRemote; private final boolean basePolicyIsConcurrent; private final PermissionCollection policyPermissions; - private final boolean loggable; /** - * Creates a new <code>DynamicPolicyProvider</code> instance that wraps + * Creates a RemotePolicyProvider instance using the System property + * "org.apache.river.api.security.RemotePolicyProvider.basePolicyClass" + * to instantiate a base Policy, otherwise if the property is not set + * creates an instance of ConcurrentPolicyFile to use as the base Policy. + * + * @throws PolicyInitializationException + */ + public RemotePolicyProvider() throws PolicyInitializationException { + String cname = Security.getProperty(basePolicyClassProperty); + if (cname == null) { + cname = defaultBasePolicyClass; + } + try { + this.basePolicy = (Policy) Class.forName(cname).newInstance(); + } catch (SecurityException e) { + throw e; + } catch (Exception e) { + throw new PolicyInitializationException( + "unable to construct base policy", e); + } + remotePolicyGrants = new PermissionGrant[0]; + grantLock = new Object(); + remotePolicyPermission = new PolicyPermission("Remote"); + protectionDomainPermission = new RuntimePermission("getProtectionDomain"); + basePolicyIsRemote = basePolicy instanceof RemotePolicy ?true: false; + basePolicyIsConcurrent = basePolicy instanceof ScalableNestedPolicy ; + policyPermissions = basePolicy.getPermissions(policyDomain); + policyPermissions.setReadOnly(); + } + + /** + * Creates a new <code>RemotePolicyProvider</code> instance that wraps * around the given non-<code>null</code> base policy object. * * @param basePolicy base policy object containing information about @@ -95,7 +133,6 @@ public class RemotePolicyProvider extend public RemotePolicyProvider(Policy basePolicy){ this.basePolicy = basePolicy; remotePolicyGrants = new PermissionGrant[0]; - loggable = logger.isLoggable(Level.FINEST); grantLock = new Object(); remotePolicyPermission = new PolicyPermission("Remote"); protectionDomainPermission = new RuntimePermission("getProtectionDomain"); @@ -117,14 +154,17 @@ public class RemotePolicyProvider extend * where two permissions combined also implied a third permission, that * neither administrator intended to grant. */ + try { // Delegating to the underlying policy is not supported. processRemotePolicyGrants(grants); - // If we get to here, the caller has permission. - } catch (SecurityException e){ - throw new RemoteException("Policy update failed", (Throwable) e); - } catch (NullPointerException e) { - throw new RemoteException("Policy update failed", (Throwable) e); + // If we get here, the caller has permission. + } catch (SecurityException ex){ + ex.fillInStackTrace(); + logger.log(Level.WARNING, "Remote Policy update failed with SecurityException: ", ex); + } catch (NullPointerException ex) { + ex.fillInStackTrace(); + logger.log(Level.SEVERE, "Remote Policy update failed with NullPointerException: ", ex); } } @@ -144,58 +184,28 @@ public class RemotePolicyProvider extend // changes between now and gaining the lock, only the length of the // HashSet is potentially not optimal, keeping the HashSet creation // outside of the lock reduces the lock held duration. - Set<ProtectionDomain> domains = null; + List<PermissionGrant> holder + = new LinkedList<PermissionGrant>(); + remotePolicyPermission.checkGuard(null); + protectionDomainPermission.checkGuard(null); + Iterator<PermissionGrant> gi = holder.iterator(); int l = grants.length; - for (int i = 0; i < l; i++ ){ - if (grants[i] == null ) throw new NullPointerException("null PermissionGrant prohibited"); - // This causes a ProtectionDomain security check. - final Class c = grants[i].getClass(); - domains = AccessController.doPrivileged( - new PrivilegedAction<Set<ProtectionDomain>>() { - public Set<ProtectionDomain> run() { - Class[] classes = c.getDeclaredClasses(); - Set<ProtectionDomain> domains = new HashSet<ProtectionDomain>(); - int l = classes.length; - for ( int i = 0; i < l; i++ ){ - domains.add(classes[i].getProtectionDomain()); - } - return domains; - } - }); - } - Iterator<ProtectionDomain> it = domains.iterator(); - while (it.hasNext()){ - if ( ! it.next().implies(remotePolicyPermission)) { - throw new SecurityException("Missing permission: " - + remotePolicyPermission.toString()); + for (int i =0; i<l; i++){ + try { + checkCallerHasGrants(grants[i]); + holder.add(grants[i]); + }catch (SecurityException e){ + logger.log(Level.WARNING, "Caller doesn't have necessary GrantPermission:\n ", grants[i]); } } - HashSet<PermissionGrant> holder - = new HashSet<PermissionGrant>(grants.length); - holder.addAll(Arrays.asList(grants)); - checkCallerHasGrants(holder); - PermissionGrant[] old = null; synchronized (grantLock) { - old = remotePolicyGrants; PermissionGrant[] updated = new PermissionGrant[holder.size()]; remotePolicyGrants = holder.toArray(updated); } - Collection<PermissionGrant> oldGrants = new HashSet<PermissionGrant>(old.length); - oldGrants.addAll(Arrays.asList(old)); - oldGrants.removeAll(holder); - // Collect removed Permission's to notify CachingSecurityManager. - Set<Permission> removed = new HashSet<Permission>(120); - Iterator<PermissionGrant> rgi = oldGrants.iterator(); - while (rgi.hasNext()){ - PermissionGrant g = rgi.next(); - removed.addAll(g.getPermissions()); - } - SecurityManager sm = System.getSecurityManager(); if (sm instanceof CachingSecurityManager) { ((CachingSecurityManager) sm).clearCache(); } - // oldGrants now only has the grants which have been removed. } @Override
