river-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From Peter Firmstone <j...@zeus.net.au>
Subject Re: Learnings from a RevokeableDynamicPolicy & A Future Roadmap
Date Wed, 11 Aug 2010 06:56:54 GMT
Fred,

Any thoughts?  The ExecutionContextManager interface and implementation 
(Controller) are shown below, the RevokeableDynamicPolicy and 
PermissionGrant interfaces have implementations available on svn.

The ExecutionContextManager is based around our discussion.

Note that no Security Delegate is provided here, just a tool, which a 
delegate or any other object may use, 
ExecutionContextManager.checkPermission() and another method 
addAction(Runnable r), to accept a Runnable to perform any clean up 
tasks for complete revocation such as closing Socket's etc.  The 
delegate need not implement any interface other than the object it 
encapsulates.

Cheers,

Peter.

package org.apache.river.api.security;

import java.security.Permission;
import java.util.List;

/**
 * RevokeableDynamicPolicy, is a Java Security Policy Provider that supports
 * Runtime Dynamically Grantable and Revokeable Permission's, in the form
 * of PermissionGrant's
 *
 * @author Peter Firmstone
 * @see java.security.Policy
 * @see java.security.ProtectionDomain
 * @see java.security.AccessController
 * @see java.security.DomainCombiner
 * @see java.security.AccessControlContext
 * @see java.security.Permission
 */
public interface RevokeableDynamicPolicy {
    /**
     * Grant Permission's as specified in a List of PermissionGrant's
     * which can be added by concurrent threads.
     *
     * @param grants
     */
    public void grant(List<PermissionGrant> grants);
    /**
     * Revoke, only removes any PermissionGrant's that are identical, 
typically
     * a List of Grant's is obtained by getPermssionGrant's which can be
     * manipulated and investigated, any that are undesirable should be 
passed
     * to revoke.
     *
     * Revokes can only be performed synchronuously with other Revokes.
     *
     * @param grants
     */
    public void revoke(List<PermissionGrant> grants);
    /**
     * Get a List copy of the current PermissionGrant's in force.
     * @return
     */
    public List<PermissionGrant> getPermissionGrants();
    /**
     * The Revocation of Permission's requires a new construct for 
controlling
     * access.  Typically many objects that provide privileged functionality
     * are guarded in their constructor by a checkPermission(Permission) 
call
     * or by a GuardedObject, once this check has succeeded, the caller 
receives
     * a reference to the guarded object.  These Permission's cannot be
     * revoked completely, because the reference has escaped, the 
permission
     * check will not be called again.
     *
     * Instead what is needed is a permission check that is efficient enough
     * to allow the methods that provide the privileged functionality to be
     * called for every method invocation.  What the ExecutionContextManager
     * does is minimise the checkPermission calls by skipping 
checkPermission for
     * any execution AccessControlContext that has already passed, unless
     * a Permission related to the one being managed is revoked, in 
which case
     * the cache of AccessControlContext's previously checked are cleared.
     *
     * The ExecutionContextManager is specific only to one permission, this
     * is the enabler for the reduced checkPermission calls, since a
     * Permission should behave in a persistent manner, once it passes, it
     * should always pass, unless revoked.
     *
     *
     * @param p Permission the ExecutionContextManager will check.
     * @return a new ExecutionContextManager instance.
     */
    public ExecutionContextManager getExecutionContextManager(Permission p);
    /**
     *
     * @return true if Revoke supported.
     */
    public boolean revokeSupported();
}

/*
 * 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 org.apache.river.api.security;

import java.security.AccessControlException;
import java.security.Permission;

/**
 * <p>
 * An ExecutionContextManager is designed to be repeatedly called, where 
calling
 * AccessController.checkPermission(Permission) is too great an overhead.
 * </p><p>
 * The ExecutionContextManager will only call
 * AccessControlContext.checkPermission(Permission) once, for each 
context.  This
 * ensures that checkPermission isn't called again until the context 
changes, or
 * the Permission checked by this ExecutionContextManager experiences a
 * revoke for any ProtectionDomain by the RevokeableDynamicPolicy.
 * </p><p>
 * A Runnable may be submitted to the ExecutionContextManager to be executed
 * when a Permission Revocation matching the stored Permission occurs.
 * </p><p>
 * Use of this class is not limited to Revokeable Permission's.
 * </p>
 * @author Peter Firmstone
 * @see RevokeableDynamicPolicy
 * @see Permission
 * @see AccessControlContext
 */
public interface ExecutionContextManager {

    /**
     * <p>
     * This is a call made by a Security Delegate, or other Object used to
     * control access to privileged methods or constructors, similar to the
     * AccessControll.checkPermission(Permission) call, but with the 
Permission
     * pre defined and unchanging.  The Permission check is optimised,
     * typically a method may only be concerned with a single Permission 
check,
     * but in many existing cases, the AccessController check is too 
expensive
     * to be called on every method invocation.  The 
ExecutionContextManager
     * should optimise this call by ensuring that 
checkPermission(Permission) is only
     * called once per AccessControlContext.  In other words if the caller's
     * AccessControlContext hasn't changed, then checkPermission(Permission)
     * isn't called again as it would be assumed to succeed, unless the
     * RevokeableDynamicPolicy revokes a Permission with the same class,
     * in which case the Permission must be checked again.
     * </p><p>
     * Typically where it is not feasable to call 
AccessController.checkPermission
     * on every invocation, those objects are usually guarded or have the
     * checkPermission method called in the constructor.
     * </p><p>
     * ExecutionContextManager provides a more thorough form of protection.
     * </p><p>
     * ExecutionContextManager should be used sparingly, the more generic
     * or widely applicable the Permission, the more efficient the
     * ExecutionContextManager is in memory usage terms.
     * </p>
     * @throws java.security.AccessControlException
     */
    public void checkPermission() throws AccessControlException;

    /**
     * Get the Permission monitored by this ExecutionContextManager.
     * @return Permission monitored by the ExecutionContextManager
     */
    public Permission getPermission();

    /**
     * <p>
     * Action to be taken in event that the Permission monitored by this
     * ExecutionContextManager has experienced a revocation event.  This
     * allows Sockets to be closed, or any clean up to occur or any other
     * task that must be performed to reset state.
     * </p><p>
     * This may not be the only action performed, since the same
     * ExecutionContextManager may be used by multiple clients, the Runnable
     * is only weakly referenced, garbage collection is relied upon for its
     * removal.
     * </p><p>
     * The implementer must have the Permission's required for
     * execution, no privileges are assigned.
     * </p>
     * @param r - Clean up task to be performed.
     */
    public void addAction(Runnable r);
}


/*
 * 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 org.apache.river.imp.security.policy.se;

import java.lang.ref.WeakReference;
import java.security.AccessControlContext;
import java.security.AccessControlException;
import java.security.AccessController;
import java.security.Permission;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.concurrent.ConcurrentMap;
import org.apache.river.api.security.ExecutionContextManager;
import org.apache.river.imp.util.ConcurrentWeakIdentityMap;

/**
 * An implementation of ExecutionContextManager.
 *
 * @author Peter Firmstone.
 */
class Controller implements ExecutionContextManager {
    /* The class manages revoke for all it's objects. */
    private static final ConcurrentMap<Permission, 
ExecutionContextManager> pool
        = new ConcurrentWeakIdentityMap<Permission, 
ExecutionContextManager>();
   
    private static final List<WeakReference<Controller>> cont =
        new ArrayList<WeakReference<Controller>>(80);
    /**
     * This method is called by the RevokeableDynamicPolicy to get a List
     * of Runnable clean up jobs that must be performed to complete the
     * revocation of Permissions.
     *
     * A Thread pool can be used to perform the work, however the policy's
     * revoke call must not return until after all the jobs are complete.
     *
     * @param classes - A set of Permission Class object's for the 
Permission's
     * being revoked.
     * @return A List of Runnable clean up tasks to be performed to complete
     * the revocation.
     */
    static List<Runnable> revoke(Set<Class> classes){
    List<Runnable> cleanupJobs;
    synchronized (cont){
        cleanupJobs = new ArrayList<Runnable>(cont.size());
        Iterator<WeakReference<Controller>> it =
            cont.iterator();
        while (it.hasNext()) {
        WeakReference<Controller> wf = it.next();
        Controller ecm = wf.get();
        if ( ecm != null ){
            if (classes.contains(ecm.getPermission().getClass())){
            ecm.clear();
            cleanupJobs.addAll(ecm.getRunnable());
            }
        } else {
            it.remove();
        }
        }
    }
    return cleanupJobs;   
    }
   
    static ExecutionContextManager getECManager(Permission p){
    ExecutionContextManager exm = pool.get(p);
    if ( exm != null ){
        return exm;
    }
    exm = new Controller(p);
    ExecutionContextManager existed = pool.putIfAbsent(p, exm);
    if ( existed != null){
        exm = existed;
    }
    return exm;   
    }
   
    /* Object state and methods */
    private final Permission perm;
    private final List<WeakReference<Runnable>> cleanup;
    private final Set<AccessControlContext> checkedExecutionContext;
   
    /* A Revocation in progress will delay construction of a new 
Controller */
    private Controller(Permission p){
    perm = p;
    WeakReference<Controller> mac =
        new WeakReference<Controller>(this);
    cleanup = new ArrayList<WeakReference<Runnable>>();
    checkedExecutionContext = new HashSet<AccessControlContext>(80);
    // this must be done last.
    synchronized (cont){
        cont.add(mac);
    }
    }

    public void checkPermission() throws AccessControlException {
    AccessControlContext executionContext = AccessController.getContext();
    synchronized (checkedExecutionContext){
        if (checkedExecutionContext.contains(executionContext)) {
        return;
        }
    }
    executionContext.checkPermission(perm);
    /* If we get to here without exception we can add it.
     * The RevokeablyDynamicPolicy will revoke the Permission prior to
     * clearing the current checked execution context as part of the revoke
     * process, this means
     * that while the revocation isn't yet complete, the checked context
     * will still return true, but the actual permission check would return
     * false, however at the completion of the revocation process, the
     * Runnable provided will be executed to perform any necessary 
cleanupJobs
     * to fully revoke.
     *
     * This prevents invalid execution context's from finding their way
     * into the checked Execution Context which would be a security breach.
     *
     * Revoke's happen synchronuously to ensure that this contract is 
honored.
     */
    synchronized (checkedExecutionContext){
        checkedExecutionContext.add(executionContext);
    }
    }

    public Permission getPermission() {
    return perm;
    }

    public void addAction(Runnable r) {
    synchronized (cleanup){
        cleanup.add(new WeakReference<Runnable>(r));
    }
    }
   
    private List<Runnable> getRunnable(){
    List<Runnable> tasks;
    synchronized (cleanup){
        tasks = new ArrayList<Runnable>(cleanup.size());
        Iterator<WeakReference<Runnable>> it = cleanup.iterator();
        while (it.hasNext()){
        WeakReference<Runnable> wr = it.next();
        Runnable r = wr.get();
        if (r != null){
            tasks.add(r);
        }else {
            cleanup.remove(wr);
        }
        }
    }
    return tasks;
    }
   
    private void clear(){
    synchronized (checkedExecutionContext){
        checkedExecutionContext.clear();
    }
    }

}

/*
 * 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 org.apache.river.api.security;

import java.security.CodeSource;
import java.security.Permission;
import java.security.Principal;
import java.security.ProtectionDomain;

/**
 * PermissionGrant's are expected to be effectively immutable,
 * threadsafe and have a good hashCode implementation to perform well in
 * Collections.
 *
 * You shouldn't pass around PermissionGrant's to just anyone as they can
 * provide an attacker with information about which Permission's may be 
granted.
 *
 * @author Peter Firmstone
 */
public interface PermissionGrant {

    /**
     * A DynamicPolicy implementation can use a PermissionGrant as a 
container
     * for Dynamic Grant's.  A PermissionGrant is first asked by the Policy
     * if it applies to a Particular ProtectionDomain, if it does, the 
Policy
     * calls getPermissions.
     *
     * Dynamic grants can be denied to some ProtectionDomains based on
     * CodeSource or URL's for codebases, this is to remove the 
possiblity of
     * executing Permissions for vulnerable codebases, once a vulnerability
     * is identified after the fact.
     *
     * @param pd ProtectionDomain
     * @return
     * @see RevokeableDynamicPolicy
     */
    boolean implies(ProtectionDomain pd); 
    /**
     * Checks if this PermissionGrant applies to the passed in ClassLoader
     * and Principal's.
     *
     * Note that if this method returns false, it doesn't necessarily mean
     * that the grant will not apply to the ClassLoader, since it will 
depend on
     * the contents of the ClassLoader and that is indeterminate. It just
     * indicates that the grant definitely does apply if it returns true.
     *
     * If this method returns false, follow up using the 
ProtectionDomain for a
     * more specific test, which may return true.
     */
    boolean implies(ClassLoader cl, Principal[] pal);
    /**
     * Checks if this PermissionGrant applies to the passed in CodeSource
     * and Principal's.
     * @param cs
     * @return
     */
    boolean implies(CodeSource codeSource, Principal[] pal);

    @Override
    boolean equals(Object o);

    /**
     * Returns an unmodifiable collection of permissions defined by this
     * PermissionGrant, may be <code>null</code>.
     * @return
     */
    Permission[] getPermissions();

    @Override
    int hashCode();

    /**
     * Returns true if this PermissionGrant defines no Permissions, or if
     * a PermissionGrant was made to a ProtectionDomain that no longer 
exists.
     */
    boolean isVoid();

}


Mime
View raw message