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