You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@river.apache.org by pe...@apache.org on 2012/11/02 11:28:22 UTC
svn commit: r1404911 - in
/river/jtsk/trunk/src/org/apache/river/api/security: AbstractPolicy.java
RemotePolicy.java RemotePolicyProvider.java
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