You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@river.apache.org by Peter Firmstone <pe...@zeus.net.au> on 2012/01/08 12:40:13 UTC

Non Blocking java.security.Policy - synchronized method is superclass.

Appended is a new java.security.Policy implementation, it fully supports
the existing java policy syntax and accepts alternate PolicyParser's. 

All state is immutable, except for 2 volatile references, referents
replaced, not mutated, when the policy is updated.  One referent is an
array containing PermissionGrant's (interface for immutable object
representing a grant statement in a policy), the second a
PermissionCollection containing the Policy Permissions.  The array is
never mutated after creation, a reference to the array is copied before
accessing the array or any array methods. 

The policy creates PermissionCollection's on demand for checking,
Permission's are ordered using a PermissionComparator to ensure that for
example, wildcard SocketPermission's are checked first, to avoid
unnecessary DNS lookups.  Only the permission being checked and any
UnresolvedPermission's are added to the PermissionCollection, limiting
the size of the objects created. 

In existing policy implementations PermissionCollection's perform
blocking operations. 

Also, after parsing policy files, PermissionGrant implementations avoid
the need to open files or network connections to confirm URL's, eg
CodeSource.implies is not called, but instead reimplemented using URI. 

Will this scale?  There but one smell: 

ProtectionDomain uses a synchronized method Policy.getPolicyNoCheck(),
but this only retrieves a reference on 99% of occasions. 

For every permission check, the stack access control context is
retrieved, every ProtectionDomain on the stack must be checked,
ProtectionDomain's must call getPolicyNoCheck() to call
Policy.implies(ProtectionDomain domain, Permission permission). 

To make this worse, I've got a SecurityManager that divides the
ProtectionDomain.implies() calls into tasks and submits them to an
executor (if there are 4 or more PD's in a context).  The
SecurityManager is also non blocking, at least it will be when I use the
new ConcurrentHashMap for the checked permission cache (avoids repeated
security checks), for now the cache is implemented using the existing
ConcurrentHashMap, but is mostly read in any case.  (P.S. This is the
cache I'm using the Reference Collection's for.) 

How much can this one synchronized method spoil scalability? 

Cheers & thanks in advance, 

Peter. 



/* 
*  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. 
*/ 

/** 
 * Default Policy implementation taken from Apache Harmony, refactored
for 
 * concurrency. 
 * 
 * @author Alexey V. Varlamov 
 * @author Peter Firmstone 
 * @version $Revision$ 
 */ 

package net.jini.security.policy; 

import java.io.File; 
import java.net.URL; 
import java.security.AccessController; 
import java.security.AllPermission; 
import java.security.CodeSource; 
import java.security.Guard; 
import java.security.Permission; 
import java.security.PermissionCollection; 
import java.security.Permissions; 
import java.security.Policy; 
import java.security.PrivilegedActionException; 
import java.security.PrivilegedExceptionAction; 
import java.security.ProtectionDomain; 
import java.security.SecurityPermission; 
import java.security.UnresolvedPermission; 
import java.util.ArrayList; 
import java.util.Collection; 
import java.util.Enumeration; 
import java.util.Iterator; 
import java.util.List; 
import java.util.NavigableSet; 
import java.util.Properties; 
import java.util.TreeSet; 
import net.jini.security.PermissionComparator; 
import org.apache.river.api.security.PermissionGrant; 
import org.apache.river.impl.security.policy.util.DefaultPolicyParser; 
import org.apache.river.impl.security.policy.util.PolicyParser; 
import org.apache.river.impl.security.policy.util.PolicyUtils; 


/** 
* Concurrent Policy implementation based on policy configuration files, 
* it is intended to provide concurrent implies() for greatly improved 
* throughput at the expense of single threaded performance. 
* 
* Set the following system properties to use this Policy instead of the 
* built in Java sun.security.provider.PolicyFile: 
* * net.jini.security.policy.PolicyFileProvider.basePolicyClass = 
* org.apache.river.security.concurrent.ConcurrentPolicyFile 
* 
* 
* This 
* implementation recognizes text files, consisting of clauses with the 
* following syntax: 
* 
* <pre> 
* keystore &quot;some_keystore_url&quot; [, &quot;keystore_type&quot;]; 
* </pre> 
<pre> 
* grant [SignedBy &quot;signer_names&quot;] [, CodeBase &quot;URL&quot;]
*  [, Principal [principal_class_name] &quot;principal_name&quot;] 
*  [, Principal [principal_class_name] &quot;principal_name&quot;] ... {
*  permission permission_class_name [ &quot;target_name&quot; ] [,
&quot;action&quot;] 
*  [, SignedBy &quot;signer_names&quot;]; 
*  permission ... 
*  }; 
* * </pre> 
* 
* The <i>keystore </i> clause specifies reference to a keystore, which
is a 
* database of private keys and their associated digital certificates.
The 
* keystore is used to look up the certificates of signers specified in
the 
* <i>grant </i> entries of the file. The policy file can contain any
number of 
* <i>keystore </i> entries which can appear at any ordinal position.
However, 
* only the first successfully loaded keystore is used, others are
ignored. The 
* keystore must be specified if some grant clause refers to a
certificate's 
* alias. <br> 
* The <i>grant </i> clause associates a CodeSource (consisting of an URL
and a 
* set of certificates) of some executable code with a set of Permissions
which 
* should be granted to the code. So, the CodeSource is defined by values
of 
* <i>CodeBase </i> and <i>SignedBy </i> fields. The <i>CodeBase </i>
value must 
* be in URL format, while <i>SignedBy </i> value is a (comma-separated
list of) 
* alias(es) to keystore certificates. These fields can be omitted to
denote any 
* codebase and any signers (including case of unsigned code),
respectively. 
* <br> 
* Also, the code may be required to be executed on behalf of some
Principals 
* (in other words, code's ProtectionDomain must have the array of
Principals 
* associated) in order to possess the Permissions. This fact is
indicated by 
* specifying one or more <i>Principal </i> fields in the <i>grant </i>
clause. 
* Each Principal is specified as class/name pair; name and class can be
either 
* concrete value or wildcard <i>* </i>. As a special case, the class
value may 
* be omitted and then the name is treated as an alias to X.509
Certificate, and 
* the Principal is assumed to be javax.security.auth.x500.X500Principal
with a 
* name of subject's distinguished name from the certificate. <br> 
* The order between the <i>CodeBase </i>, <i>SignedBy </i>, and
<i>Principal 
* </i> fields does not matter. The policy file can contain any number of
grant 
* clauses. <br> 
* Each <i>grant </i> clause must contain one or more <i>permission </i>
entry. 
* The permission entry consist of a fully qualified class name along
with 
* optional <i>name </i>, <i>actions </i> and <i>signedby </i> values.
Name and 
* actions are arguments to the corresponding constructor of the
permission 
* class. SignedBy value represents the keystore alias(es) to
certificate(s) 
* used to sign the permission class. That is, this permission entry is 
* effective (i.e., access control permission will be granted based on
this 
* entry) only if the bytecode implementation of permission class is
verified to 
* be correctly signed by the said alias(es). <br> 
* <br> 
* The policy content may be parameterized via property expansion.
Namely, 
* expressions like <i>${key} </i> are replaced by values of
corresponding 
* system properties. Also, the special <i>slash </i> key (i.e. ${/}) is 
* supported, it is a shortcut to &quot;file.separator&quot; key.
Property 
* expansion is performed anywhere a double quoted string is allowed in
the 
* policy file. However, this feature is controlled by security
properties and 
* should be turned on by setting &quot;policy.expandProperties&quot;
property 
* to <i>true </i>. <br> 
* If property expansion fails (due to a missing key), a corresponding
entry is 
* ignored. For fields of <i>keystore </i> and <i>grant </i> clauses, the
whole 
* clause is ignored, and for <i>permission </i> entry, only that entry
is 
* ignored. <br> 
* <br> 
* The policy also supports generalized expansion in permissions names,
of 
* expressions like <i>${{protocol:data}} </i>. Currently the following 
* protocols supported: 
* <dl> 
* <dt>self 
* <dd>Denotes substitution to a principal information of the parental
Grant 
* entry. Replaced by a space-separated list of resolved Principals
(including 
* wildcarded), each formatted as <i>class &quot;name&quot; </i>. If
parental 
* Grant entry has no Principals, the permission is ignored. 
* <dt>alias: <i>name </i> 
* <dd>Denotes substitution of a KeyStore alias. Namely, if a KeyStore
has an 
* X.509 certificate associated with the specified name, then replaced by
* <i>javax.security.auth.x500.X500Principal &quot; <i>DN </i>&quot; </i>
* string, where <i>DN </i> is a certificate's subject distinguished
name. 
* </dl> 
* <br> 
* 
*/ 

public class ConcurrentPolicyFile extends Policy implements
ConcurrentPolicy { 

   /** 
    * System property for dynamically added policy location. 
    */ 
   private static final String JAVA_SECURITY_POLICY =
"java.security.policy"; //$NON-NLS-1$ 

   /** 
    * Prefix for numbered Policy locations specified in
security.properties. 
    */ 
   private static final String POLICY_URL_PREFIX = "policy.url.";
//$NON-NLS-1$ 
     // Reference must be defensively copied before access, once
published, never mutated. 
   private volatile PermissionGrant [] grantArray; 
     // A specific parser for a particular policy file format. 
   private final PolicyParser parser; 
     private static final Guard guard = new
SecurityPermission("getPolicy"); 
     private final ProtectionDomain myDomain; 
     // reference must be defensively copied before access, once
published, never mutated. 
   private volatile PermissionCollection myPermissions; 
     /** 
    * Default constructor, equivalent to 
    * <code>ConcurrentPolicyFile(new DefaultPolicyParser())</code>. 
    */ 
   public ConcurrentPolicyFile() throws PolicyInitializationException { 
       this(new DefaultPolicyParser()); 
   } 

   /** 
    * Extension constructor for plugging-in a custom parser. 
    * @param dpr 
    */ 
   protected ConcurrentPolicyFile(PolicyParser dpr) throws
PolicyInitializationException { 
       guard.checkGuard(null); 
       parser = dpr; 
       myDomain = this.getClass().getProtectionDomain(); 
       /* 
        * The bootstrap policy makes implies decisions until this
constructor 
        * has returned.  We don't need to lock. 
        */ 
       try { 
           // Bug 4911907, do we need to do anything more? 
           // The permissions for this domain must be retrieved before 
           // construction is complete and this policy takes over. 
           initialize(); // Instantiates myPermissions. 
       } catch (SecurityException e){ 
           throw e; 
       } catch (Exception e){ 
           throw new PolicyInitializationException("PolicyInitialization
failed", e); 
       } 
   } 
     private PermissionCollection convert(NavigableSet<Permission>
permissions){ 
       PermissionCollection pc = new Permissions(); 
       // The descending iterator is for SocketPermission. 
       Iterator<Permission> it = permissions.descendingIterator(); 
       while (it.hasNext()) { 
           pc.add(it.next()); 
       } 
       return pc; 
   } 

   /** 
    * Returns collection of permissions allowed for the domain 
    * according to the policy. The evaluated characteristics of the 
    * domain are it's codesource and principals; they are assumed 
    * to be <code>null</code> if the domain is <code>null</code>. 
    * 
    * Each PermissionCollection returned is a unique instance. 
    * 
    * @param pd ProtectionDomain 
    * @see ProtectionDomain 
    */ 
   @Override 
   public PermissionCollection getPermissions(ProtectionDomain pd) { 
       NavigableSet<Permission> perms = new TreeSet<Permission>(new
PermissionComparator()); 
       PermissionGrant [] grantRefCopy = grantArray; 
       int l = grantRefCopy.length; 
       for ( int j =0; j < l; j++ ){ 
           PermissionGrant ge = grantRefCopy[j]; 
           if (ge.implies(pd)){ 
               if (ge.isPrivileged()){// Don't stuff around finish early
if you can. 
                   PermissionCollection pc = new Permissions(); 
                   pc.add(new AllPermission()); 
                   return pc; 
               } 
               Collection<Permission> c = ge.getPermissions(); 
               Iterator<Permission> i = c.iterator(); 
               while (i.hasNext()){ 
                   Permission p = i.next(); 
                   perms.add(p); 
               } 
           } 
       } 
       // Don't forget to merge the static Permissions. 
       PermissionCollection staticPC = null; 
       if (pd != null) { 
           staticPC = pd.getPermissions(); 
           if (staticPC != null){ 
               Enumeration<Permission> e = staticPC.elements(); 
               while (e.hasMoreElements()){ 
                   Permission p = e.nextElement(); 
                   if (p instanceof AllPermission) { 
                       PermissionCollection pc = new Permissions(); 
                       pc.add(p); 
                       return pc; 
                   } 
                   perms.add(p); 
               } 
           } 
       } 
       return convert(perms); 
   } 

   /** 
    * Returns collection of permissions allowed for the codesource 
    * according to the policy. 
    * The evaluation assumes that current principals are undefined. 
    * 
    * This returns a java.security.Permissions collection, which allows 
    * ProtectionDomain to optimise for the AllPermission case, which
avoids 
    * unnecessarily consulting the policy. 
    * 
    * If constructed with the four argument constructor,
ProtectionDomain.implies 
    * first consults the Policy, then it's own internal Permissions
collection, 
    * unless it has AllPermission, in which case it returns true without
    * consulting the policy. 
    * 
    * @param cs CodeSource 
    * @see CodeSource 
    */ 
   @Override 
   public PermissionCollection getPermissions(CodeSource cs) { 
       if (cs == null) throw new NullPointerException("CodeSource cannot
be null"); 
       NavigableSet<Permission> perms = new TreeSet<Permission>(new
PermissionComparator()); 
       // for ProtectionDomain AllPermission optimisation. 
       PermissionGrant [] grantRefCopy = grantArray; 
       int l = grantRefCopy.length; 
       for ( int j =0; j < l; j++ ){ 
           PermissionGrant ge = grantRefCopy[j]; 
           if (ge.implies(cs, null)){ // No Principal's 
               if (ge.isPrivileged()){// Don't stuff around finish early
if you can. 
                   PermissionCollection pc = new Permissions(); 
                   pc.add(new AllPermission()); 
                   return pc; 
               } 
               Collection<Permission> c = ge.getPermissions(); 
               Iterator<Permission> i = c.iterator(); 
               while (i.hasNext()){ 
                   Permission p = i.next(); 
                   perms.add(p); 
               } 
           } 
       } 
       return convert(perms); 
   } 
     @Override 
   public boolean implies(ProtectionDomain domain, Permission
permission) { 
       if (permission == null) throw new
NullPointerException("permission not allowed to be null"); 
       if (domain == myDomain) { 
           PermissionCollection pc = myPermissions; 
           return pc.implies(permission); 
       } 
       Class klass = permission.getClass(); 
       // Need to have a list of Permission's we can sort if permission
is SocketPermission. 
       NavigableSet<Permission> perms = new TreeSet<Permission>(new
PermissionComparator()); 
       PermissionGrant [] grantRefCopy = grantArray; 
       int l = grantRefCopy.length; 
       for ( int j =0; j < l; j++ ){ 
           PermissionGrant ge = grantRefCopy[j]; 
           if (ge.implies(domain)){ 
               if (ge.isPrivileged()) return true; // Don't stuff around
finish early if you can. 
               Collection<Permission> c = ge.getPermissions(); 
               Iterator<Permission> i = c.iterator(); 
               while (i.hasNext()){ 
                   Permission p = i.next(); 
                   // Don't make it larger than necessary. 
                   if (klass.isInstance(permission) || permission
instanceof UnresolvedPermission){ 
                       perms.add(p); 
                   } 
               } 
           } 
       } 
       // Don't forget to merge the static Permissions. 
       PermissionCollection staticPC = null; 
       if (domain != null) { 
           staticPC =domain.getPermissions(); 
           if (staticPC != null){ 
               Enumeration<Permission> e = staticPC.elements(); 
               while (e.hasMoreElements()){ 
                   Permission p = e.nextElement(); 
                   // return early if possible. 
                   if (p instanceof AllPermission ) return true; 
                   // Don't make it larger than necessary, but don't
worry about duplicates either. 
                   if (klass.isInstance(permission) || permission
instanceof UnresolvedPermission){ 
                       perms.add(p); 
                   } 
               } 
           } 
       } 
       return convert(perms).implies(permission); 
   } 

   /** 
    * Gets fresh list of locations and tries to load all of them in
sequence; 
    * failed loads are ignored. After processing all locations, old
policy 
    * settings are discarded and new ones come into force. <br> 
    * 
    * @see PolicyUtils#getPolicyURLs(Properties, String, String) 
    */ 
   @Override 
   public void refresh() { 
       try { 
           initialize(); 
       } catch (Exception ex) { 
           System.err.println(ex); 
       } 
   } 
     private void initialize() throws Exception{ 
       try { 
           Collection<PermissionGrant> fresh =
AccessController.doPrivileged( 
               new
PrivilegedExceptionAction<Collection<PermissionGrant>>(){ 
                   public Collection<PermissionGrant> run() throws
SecurityException { 
                       Collection<PermissionGrant> fresh = new
ArrayList<PermissionGrant>(120); 
                       Properties system = System.getProperties(); 
                       system.setProperty("/", File.separator);
//$NON-NLS-1$ 
                       URL[] policyLocations =
PolicyUtils.getPolicyURLs(system, 
                                                        
JAVA_SECURITY_POLICY, 
                                                        
POLICY_URL_PREFIX); 
                       int l = policyLocations.length; 
                       for (int i = 0; i < l; i++) { 
                           //TODO debug log 
//                                System.err.println("Parsing policy
file: " + policyLocations[i]); 
                           try { 
                               Collection<PermissionGrant> pc = null; 
                               pc = parser.parse(policyLocations[i],
system); 
                               fresh.addAll(pc); 
                           } catch (Exception e){ 
                               // It's best to let a SecurityException
bubble up 
                               // in case there is a problem with our
policy configuration 
                               // or implementation. 
                               if ( e instanceof SecurityException ) { 
                                   e.printStackTrace(System.out); 
                                   throw (SecurityException) e; 
                               } 
                               // ignore. 
                           } 
                       } 
                       return fresh; 
                   } 
               } 
           ); 
           // Volatile reference, publish after mutation complete. 
           grantArray = fresh.toArray(new
PermissionGrant[fresh.size()]); 
           myPermissions = getPermissions(myDomain); 
       }catch (PrivilegedActionException e){ 
           Throwable t = e.getCause(); 
           if ( t instanceof Exception ) throw (Exception) t; 
           throw e; 
       } 
   } 

   public boolean isConcurrent() { 
       return true; 
   } 

   public PermissionGrant[] getPermissionGrants() { 
       PermissionGrant [] grants = grantArray; // copy volatile
reference target. 
       return grants.clone(); 
   } 
     public PermissionGrant[] getPermissionGrants(ProtectionDomain pd) {
       PermissionGrant [] grants = grantArray; // copy volatile
reference target. 
       int l = grants.length; 
       List<PermissionGrant> applicable = new
ArrayList<PermissionGrant>(l); // Always too large, never too small. 
       for (int i =0; i < l; i++){ 
           if (grants[i].implies(pd)){ 
               applicable.add(grants[i]); 
           } 
       } 
       return applicable.toArray(new
PermissionGrant[applicable.size()]); 
   } 

} 


Re: Non Blocking java.security.Policy - synchronized method is superclass.

Posted by Peter Firmstone <ji...@zeus.net.au>.
Dan Creswell wrote:
> On 9 January 2012 10:03, Peter Firmstone <ji...@zeus.net.au> wrote:
>   
>> The really odd part is, I've removed the policy caches, instead of slowing
>> down like I expected, the policies run faster, I've stripped the cache from
>> DynamicPolicyProvider too, making it far simpler, will commit soon.
>>
>>     
The array of PermissionGrant's are much smaller than the cache was, a 
PermissionGrant can apply to multiple ProtectionDomains, while the 
original cache was a Map containing ProtectionDomains as keys, and 
java.security.Permissions as values, each Permission was contained in a 
PermissionCollection, referenced by another Map, containing the 
Permission classes as keys.

It must have been done like that since the original CodeSource.implies 
would have been slow, it required file and network access (File, URL & 
SocketPermission), while URIGrant (implements PermissionGrant) uses URI 
and reimplements equivalent semantics to CodeSource.implies, but doesn't 
ask the DNS server, a spoofed DNS server in this case would be more 
dangerous, than it is with SocketPermission, eg: grant AllPermission by 
matching a privileged CodeSource URL, with an IP address.

The cache still had a lot of processing to do, with 
PermissionCollection.implies calls and blocking while in use.

Its also possible to grant the same set of Permissions in java policy 
files (an undocumented feature I had to implement when a number of the 
qa tests failed):

grant codebase "file:${{some.urls}}" {
    permission java.security.AllPermission "", "";
};

Where the Property might be:
"some.urls" => 
"${river.home}/lib/jsk-platform.jar:${river.home}/lib/start.jar"

>
> Caches are not free exacting a price against the garbage collector,
> reducing memory for other operations plus CPU cycles just to maintain
> them. So like much else they bring a price and if that outweighs the
> benefits you'll see a speed-up when you remove them.
>   

That sounds right, that large cache would need to find it's way to the 
permgen heap, wouldn't fit in cache etc.   Instead of caching 
Permissions (PermissionCollection) it's now generated on demand, but 
only containing Permission objects that are instances of the checked 
Permission and UnresolvedPermissions, (except for GrantPermission, in 
which case all Permission objects are added for Umbrella grants).  The 
Permission objects are also sorted prior to being added to Permissions, 
wild card permissions are added last, so they get checked first.

PermissionGrant.isPrivileged() returns true if the grant contains 
AllPermission, so in that case the Permission's don't even need to be 
retrieved.
> I would be interested in understanding just how big that cache needs
> to be or how often it needs to be consulted for it to bring advantage.
> It could be it's only beneficial for e.g. App Servers that have
> ridiculous amounts of security load because of the way they're
> designed (and the associated assumptions).
>   

I struggled with creating ConcurrentPermissions to replace 
java.security.Permissions, there's just too much blocking in all the 
synchronized PermissionCollection's, it looks like implementing a cache 
was the wrong decision now.  Instead, I just minimise the size of 
Permissions created, so the internal Map's and List stay small, then 
discarded it after use (should fit in the L2 cache, never written to ram).

But then my system has 4 sockets (not multi core), so shared state and 
cache misses are expensive.

DelegateCombinerSecurityManager has a cache, however it only caches 
results for each AccessControlContext (not as fine grained as the 
policy) and uses a flat ConcurrentMap, that references 
ConcurrentSkipListSet's containing Permission objects, identified using 
PermissionComparator (appended).

/*
 *  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 net.jini.security;

import java.io.Serializable;
import java.security.Permission;
import java.security.UnresolvedPermission;
import java.security.cert.Certificate;
import java.util.Arrays;
import java.util.Comparator;

/**
 * A Comparator for Permission that avoids using equals and hashCode() on
 * Permission implementations.
 *
 * This comparator orders the Permission first by Class, then Name, followed
 * by Actions.
 *
 * Class is sorted by class hashcode.
 *
 * Name is sorted using Unicode character order, so wildcards "*" and
 * characters will preceed numbers, which will preceed letters.
 *
 * The comparator must be as fast as possible, the common case is not equal,
 * so that must be very fast.
 *
 * Note that for SocketPermissionCollection that the desired order to 
add to
 * SocketPermission's is in , with the most likely permissions added last. 
 *
 * HINT: Use a NavigableMap to return a reverse order iterator for
 * PermissionCollection's like SocketPermissionCollection and
 * FilePermissionCollection.
 *
 * @author Peter Firmstone.
 */
public class PermissionComparator implements Comparator<Permission>, 
Serializable {
    private static final long serialVersionUID = 1L;
    private static final char wildcard = "*".charAt(0);

    public int compare(Permission o1, Permission o2) {
        if (o1 == o2) return 0;
       
        if ( o1 == null ){
            if (o2 == null) return 0;
            return -1; // o1 is less
        }
        if ( o2 == null ) return 1; // o1 is greater
       
        int hash1, hash2, comparison;
        // Permission not equal if Class hashCode not equal.
        Class c1 = o1.getClass();
        Class c2 = o2.getClass();
        hash1 = c1.hashCode();
        hash2 = c2.hashCode();
        if (hash1 < hash2) return -1;
        if (hash1 > hash2) return 1;
        //hashcodes equal.
        if (o1 instanceof UnresolvedPermission && o2 instanceof 
UnresolvedPermission){
            // Special case
            UnresolvedPermission u1 = (UnresolvedPermission) o1, u2 = 
(UnresolvedPermission) o2;
            String type1 = u1.getUnresolvedType(), type2 = 
u2.getUnresolvedType();
            if ( type1 == null ){
                if (type2 == null) return 0;
                return -1; // o1 is less
            }
            if ( type2 == null ) return 1; // o1 is greater
            comparison = type1.compareTo(type2);
            if ( comparison != 0 ) return comparison;
            // types equal.
            String name1 = u1.getUnresolvedName(), name2 = 
u2.getUnresolvedName();
            if ( name1 == null ){
                if (name2 == null) return 0;
                return -1; // o1 is less
            }
            if ( name2 == null ) return 1; // o1 is greater
            comparison = name1.compareTo(name2);
            if ( comparison != 0 ) return comparison;
            // names equal.
            String action1 = u1.getUnresolvedName(), action2 = 
u2.getUnresolvedName();
            if ( action1 == null ){
                if (action2 == null) return 0;
                return -1; // o1 is less
            }
            if ( action2 == null ) return 1; // o1 is greater
            comparison = action1.compareTo(action2);
            if ( comparison != 0 ) return comparison;
            // actions equal.
            Certificate[] cert1 = u1.getUnresolvedCerts(), cert2 = 
u2.getUnresolvedCerts();
            if ( cert1 == null ){
                if (cert2 == null) return 0;
                return -1; // o1 is less
            }
            if ( cert2 == null ) return 1; // o1 is greater
            int l1 = cert1.length, l2 = cert2.length;
            if (l1 < l2 ) return -1;
            if (l1 > l2 ) return 1;
            // Same length cert arrays.
            if (Arrays.asList(cert1).containsAll(Arrays.asList(cert2))) 
return 0;
            // compare each until they don't match don't be fussy 
they're not equal
            // but they're the same length.
            for (int i = 0; i < l1; i++){
                int c = cert1[i].toString().compareTo(cert2[i].toString());
                if (c != 0) return c;
            }
            return -1;
        }
        String name1 = o1.getName();
        String name2 = o2.getName();
        if ( name1 == null ){
            if (name2 == null) return 0;
            return -1; // o1 is less
        }
        if ( name2 == null ) return 1; // o1 is greater
        comparison = name1.compareTo(name2);
        if ( comparison != 0 ) return comparison;
        // names equal.
        String actions1 = o1.getActions();
        String actions2 = o2.getActions();
        if ( actions1 == null ){
            if (actions2 == null) return 0;
            return -1; // o1 is less
        }
        if ( actions2 == null ) return 1; // o1 is greater
        comparison = actions1.compareTo(actions2);
        if ( comparison != 0 ) return comparison;
        // actions equal.
        // Now we must be careful that these Permission's are truly equal.
        // Check they have same class
        if ( c1.equals(c2)) return 0;
        // if we get to here, someone might be trying to substitute
        return -1;
    }

}


Re: Non Blocking java.security.Policy - synchronized method is superclass.

Posted by Dan Creswell <da...@gmail.com>.
On 9 January 2012 10:03, Peter Firmstone <ji...@zeus.net.au> wrote:
>
>> I think this is lacking context - what kind of service would one write
>> that needs this many cores and thrashes that particular lock so hard
>> it matters as compared to all the other compute it's doing?
>>
>> I'd also observe that running multiple processes gets you out of this
>> predicament.
>>
>> In essence, I'm not sure it's a problem worth worrying about until
>> it's a real-world problem worth worrying about.
>>
>
>
> I think I might just place a comment in the code with a possible solution if
> it ever becomes a problem. You're right, the added complexity isn't worth
> the hassle right now.
>

Seems like the right choice to me.

> The really odd part is, I've removed the policy caches, instead of slowing
> down like I expected, the policies run faster, I've stripped the cache from
> DynamicPolicyProvider too, making it far simpler, will commit soon.
>

Caches are not free exacting a price against the garbage collector,
reducing memory for other operations plus CPU cycles just to maintain
them. So like much else they bring a price and if that outweighs the
benefits you'll see a speed-up when you remove them.

I would be interested in understanding just how big that cache needs
to be or how often it needs to be consulted for it to bring advantage.
It could be it's only beneficial for e.g. App Servers that have
ridiculous amounts of security load because of the way they're
designed (and the associated assumptions).

> I've fixed up MergedPolicyProvider and AggregatePolicyProvider also.
>
> Cheers,
>
> Peter.
>

Re: Non Blocking java.security.Policy - synchronized method is superclass.

Posted by Peter Firmstone <ji...@zeus.net.au>.
Dan Creswell wrote:
> On 8 January 2012 22:48, Peter Firmstone <ji...@zeus.net.au> wrote:
>   
>> Dan Creswell wrote:
>>     
>>> On 8 January 2012 11:40, Peter Firmstone <pe...@zeus.net.au>
>>> wrote:
>>>
>>>       
>>>> How much can this one synchronized method spoil scalability?
>>>>
>>>>
>>>>         
>>> Not much as far as I can see - there's going to be a one off
>>> initialisation cost and after that it's a fast path with a single
>>> reference check and a return. I can't think of much that's less
>>> compute intensive and thus lower contention.
>>>
>>> I think you'd have to be running some very trivial code that called
>>> this method many many times whilst it didn't do much for it to turn up
>>> as high cost.
>>>
>>>       
>> That's what I thought as well, true on today's hardware.
>>
>> The other thing that bothers me is it's synchronized on the
>> java.security.Policy class monitor, a very effective denial of service
>> attack is to obtain the policy class lock, no permission is required, then
>> all permission checks block.  Still there are other DOS attacks that can be
>> performed on the jvm, like memory errors, although I have found it possible
>> to create an executor that can recover safely from that state.
>>
>> I'm not so sure about Tomorrow, with future processors exploiting die
>> shrinks by increasing core count.  Most of the concurrent software we write
>> today, is only scalable to about 8 cores.
>>
>> By removing the cache from the ConcurrentPolicyFile, it becomes almost
>> entirely immutable.
>>
>> The trick to scalability is to mutate in a single thread, then publish
>> immutable objects, creating non blocking code paths, so every thread can
>> proceed, which is how the new policy basically works.
>>
>> I haven't made any decisions, yet, it's just a smell that bothers me.
>>     
>
> I think this is lacking context - what kind of service would one write
> that needs this many cores and thrashes that particular lock so hard
> it matters as compared to all the other compute it's doing?
>
> I'd also observe that running multiple processes gets you out of this
> predicament.
>
> In essence, I'm not sure it's a problem worth worrying about until
> it's a real-world problem worth worrying about.
>   

I think I might just place a comment in the code with a possible 
solution if it ever becomes a problem. You're right, the added 
complexity isn't worth the hassle right now.

The really odd part is, I've removed the policy caches, instead of 
slowing down like I expected, the policies run faster, I've stripped the 
cache from DynamicPolicyProvider too, making it far simpler, will commit 
soon.

I've fixed up MergedPolicyProvider and AggregatePolicyProvider also.

Cheers,

Peter.


Re: Non Blocking java.security.Policy - synchronized method is superclass.

Posted by Peter Firmstone <ji...@zeus.net.au>.
Dan Creswell wrote:
> I'd also observe that running multiple processes gets you out of this
> predicament.
>
>   
Yes that's true too.

Since it's a global class lock on Policy, it does make it a possible 
avenue to perform denial of service.  I might just raise a bug report 
and suggest it be changed to an internal explicit lock, so it can't be 
obtained until after the permission check is performed.

Interestingly Oracle's talking about process isolation for Java 9.

Cheers,

Peter.

Re: Non Blocking java.security.Policy - synchronized method is superclass.

Posted by Peter Firmstone <ji...@zeus.net.au>.
Looks like we don't need to be concerned, they've fixed it in Java 8.

7093090
*Votes* 0
*Synopsis* Reduce synchronization in java.security.Policy.getPolicyNoCheck *
Category* java:classes_security
*Reported Against* *Release Fixed* 8(b15) *
State* 10-Fix Delivered, bug *Priority:* 2-High *Related Bugs* *Submit 
Date* 20-SEP-2011 *Description*

java.security.Policy.getPolicyNoCheck() is synchronized which causes some thread contention.
Posted Date : 2011-09-20 23:44:03.0

*Work Around*

N/A

*Evaluation*

The fix involved adding an initialized flag to indicate when the system-wide
policy has been initialized and storing both the flag and the Policy object in
an AtomicReference. Then, I also used the double-check locking idiom to avoid
locking the Policy class when the Policy had already been initialized.

Changeset: http://hg.openjdk.java.net/jdk8/tl/jdk/rev/1945abeb82a0
Posted Date : 2011-11-22 15:15:14.0




Dan Creswell wrote:
> On 8 January 2012 22:48, Peter Firmstone <ji...@zeus.net.au> wrote:
>   
>> Dan Creswell wrote:
>>     
>>> On 8 January 2012 11:40, Peter Firmstone <pe...@zeus.net.au>
>>> wrote:
>>>
>>>       
>>>> How much can this one synchronized method spoil scalability?
>>>>
>>>>
>>>>         
>>> Not much as far as I can see - there's going to be a one off
>>> initialisation cost and after that it's a fast path with a single
>>> reference check and a return. I can't think of much that's less
>>> compute intensive and thus lower contention.
>>>
>>> I think you'd have to be running some very trivial code that called
>>> this method many many times whilst it didn't do much for it to turn up
>>> as high cost.
>>>
>>>       
>> That's what I thought as well, true on today's hardware.
>>
>> The other thing that bothers me is it's synchronized on the
>> java.security.Policy class monitor, a very effective denial of service
>> attack is to obtain the policy class lock, no permission is required, then
>> all permission checks block.  Still there are other DOS attacks that can be
>> performed on the jvm, like memory errors, although I have found it possible
>> to create an executor that can recover safely from that state.
>>
>> I'm not so sure about Tomorrow, with future processors exploiting die
>> shrinks by increasing core count.  Most of the concurrent software we write
>> today, is only scalable to about 8 cores.
>>
>> By removing the cache from the ConcurrentPolicyFile, it becomes almost
>> entirely immutable.
>>
>> The trick to scalability is to mutate in a single thread, then publish
>> immutable objects, creating non blocking code paths, so every thread can
>> proceed, which is how the new policy basically works.
>>
>> I haven't made any decisions, yet, it's just a smell that bothers me.
>>     
>
> I think this is lacking context - what kind of service would one write
> that needs this many cores and thrashes that particular lock so hard
> it matters as compared to all the other compute it's doing?
>
> I'd also observe that running multiple processes gets you out of this
> predicament.
>
> In essence, I'm not sure it's a problem worth worrying about until
> it's a real-world problem worth worrying about.
>
>   
>> Regards,
>>
>> Peter.
>>
>>
>>
>>     
>
>   


Re: Non Blocking java.security.Policy - synchronized method is superclass.

Posted by Dan Creswell <da...@gmail.com>.
On 8 January 2012 22:48, Peter Firmstone <ji...@zeus.net.au> wrote:
> Dan Creswell wrote:
>>
>> On 8 January 2012 11:40, Peter Firmstone <pe...@zeus.net.au>
>> wrote:
>>
>>>
>>>
>>> How much can this one synchronized method spoil scalability?
>>>
>>>
>>
>>
>> Not much as far as I can see - there's going to be a one off
>> initialisation cost and after that it's a fast path with a single
>> reference check and a return. I can't think of much that's less
>> compute intensive and thus lower contention.
>>
>> I think you'd have to be running some very trivial code that called
>> this method many many times whilst it didn't do much for it to turn up
>> as high cost.
>>
>
> That's what I thought as well, true on today's hardware.
>
> The other thing that bothers me is it's synchronized on the
> java.security.Policy class monitor, a very effective denial of service
> attack is to obtain the policy class lock, no permission is required, then
> all permission checks block.  Still there are other DOS attacks that can be
> performed on the jvm, like memory errors, although I have found it possible
> to create an executor that can recover safely from that state.
>
> I'm not so sure about Tomorrow, with future processors exploiting die
> shrinks by increasing core count.  Most of the concurrent software we write
> today, is only scalable to about 8 cores.
>
> By removing the cache from the ConcurrentPolicyFile, it becomes almost
> entirely immutable.
>
> The trick to scalability is to mutate in a single thread, then publish
> immutable objects, creating non blocking code paths, so every thread can
> proceed, which is how the new policy basically works.
>
> I haven't made any decisions, yet, it's just a smell that bothers me.

I think this is lacking context - what kind of service would one write
that needs this many cores and thrashes that particular lock so hard
it matters as compared to all the other compute it's doing?

I'd also observe that running multiple processes gets you out of this
predicament.

In essence, I'm not sure it's a problem worth worrying about until
it's a real-world problem worth worrying about.

>
> Regards,
>
> Peter.
>
>
>

Re: Non Blocking java.security.Policy - synchronized method is superclass.

Posted by Peter Firmstone <ji...@zeus.net.au>.
Dan Creswell wrote:
> On 8 January 2012 11:40, Peter Firmstone <pe...@zeus.net.au> wrote:
>   
>>
>> How much can this one synchronized method spoil scalability?
>>
>>     
>
> Not much as far as I can see - there's going to be a one off
> initialisation cost and after that it's a fast path with a single
> reference check and a return. I can't think of much that's less
> compute intensive and thus lower contention.
>
> I think you'd have to be running some very trivial code that called
> this method many many times whilst it didn't do much for it to turn up
> as high cost.
>   
That's what I thought as well, true on today's hardware.

The other thing that bothers me is it's synchronized on the 
java.security.Policy class monitor, a very effective denial of service 
attack is to obtain the policy class lock, no permission is required, 
then all permission checks block.  Still there are other DOS attacks 
that can be performed on the jvm, like memory errors, although I have 
found it possible to create an executor that can recover safely from 
that state.

I'm not so sure about Tomorrow, with future processors exploiting die 
shrinks by increasing core count.  Most of the concurrent software we 
write today, is only scalable to about 8 cores.

By removing the cache from the ConcurrentPolicyFile, it becomes almost 
entirely immutable.

The trick to scalability is to mutate in a single thread, then publish 
immutable objects, creating non blocking code paths, so every thread can 
proceed, which is how the new policy basically works.

I haven't made any decisions, yet, it's just a smell that bothers me.

Regards,

Peter.




Re: Non Blocking java.security.Policy - synchronized method is superclass.

Posted by Dan Creswell <da...@gmail.com>.
On 8 January 2012 11:40, Peter Firmstone <pe...@zeus.net.au> wrote:
> Appended is a new java.security.Policy implementation, it fully supports
> the existing java policy syntax and accepts alternate PolicyParser's.
>
> All state is immutable, except for 2 volatile references, referents
> replaced, not mutated, when the policy is updated.  One referent is an
> array containing PermissionGrant's (interface for immutable object
> representing a grant statement in a policy), the second a
> PermissionCollection containing the Policy Permissions.  The array is
> never mutated after creation, a reference to the array is copied before
> accessing the array or any array methods.
>
> The policy creates PermissionCollection's on demand for checking,
> Permission's are ordered using a PermissionComparator to ensure that for
> example, wildcard SocketPermission's are checked first, to avoid
> unnecessary DNS lookups.  Only the permission being checked and any
> UnresolvedPermission's are added to the PermissionCollection, limiting
> the size of the objects created.
>
> In existing policy implementations PermissionCollection's perform
> blocking operations.
>
> Also, after parsing policy files, PermissionGrant implementations avoid
> the need to open files or network connections to confirm URL's, eg
> CodeSource.implies is not called, but instead reimplemented using URI.
>
> Will this scale?  There but one smell:
>
> ProtectionDomain uses a synchronized method Policy.getPolicyNoCheck(),
> but this only retrieves a reference on 99% of occasions.
>
> For every permission check, the stack access control context is
> retrieved, every ProtectionDomain on the stack must be checked,
> ProtectionDomain's must call getPolicyNoCheck() to call
> Policy.implies(ProtectionDomain domain, Permission permission).
>
> To make this worse, I've got a SecurityManager that divides the
> ProtectionDomain.implies() calls into tasks and submits them to an
> executor (if there are 4 or more PD's in a context).  The
> SecurityManager is also non blocking, at least it will be when I use the
> new ConcurrentHashMap for the checked permission cache (avoids repeated
> security checks), for now the cache is implemented using the existing
> ConcurrentHashMap, but is mostly read in any case.  (P.S. This is the
> cache I'm using the Reference Collection's for.)
>
> How much can this one synchronized method spoil scalability?
>

Not much as far as I can see - there's going to be a one off
initialisation cost and after that it's a fast path with a single
reference check and a return. I can't think of much that's less
compute intensive and thus lower contention.

I think you'd have to be running some very trivial code that called
this method many many times whilst it didn't do much for it to turn up
as high cost.

> Cheers & thanks in advance,
>
> Peter.
>

Re: [concurrency-interest] Non Blocking java.security.Policy - synchronized method is superclass.

Posted by Peter Firmstone <pe...@zeus.net.au>.
It's not causing any noticeable problems for me on 4 cpu's, looks like
it caused enough contention for someone to report it though, it's fixed
in Java 8.

We have a new SecurityManager that checks ProtectionDomain's in the
AccessControlContext in parallel, rather than in series, if there are
ten ProtectionDomain's on the stack, that would increase the number of
threads accessing the lock by a factor of ten.

So the potentials certainly there.

-Peter.

7093090 
*Votes* 0 
*Synopsis* Reduce synchronization in
java.security.Policy.getPolicyNoCheck * 
Category* java:classes_security 
*Reported Against* *Release Fixed* 8(b15) * 
State* 10-Fix Delivered, bug *Priority:* 2-High *Related Bugs* *Submit
Date* 20-SEP-2011 *Description*

java.security.Policy.getPolicyNoCheck() is synchronized which causes
some thread contention. 
Posted Date : 2011-09-20 23:44:03.0 

*Work Around*

N/A 

*Evaluation*

The fix involved adding an initialized flag to indicate when the
system-wide 
policy has been initialized and storing both the flag and the Policy
object in 
an AtomicReference. Then, I also used the double-check locking idiom to
avoid 
locking the Policy class when the Policy had already been initialized. 

Changeset: http://hg.openjdk.java.net/jdk8/tl/jdk/rev/1945abeb82a0
Posted Date : 2011-11-22 15:15:14.0 
On Tue, 2012-01-10 at 03:22, Nathan Reynolds wrote:
> > How much can this one synchronized method spoil scalability? 
> 
> Depends upon the workload.  Some workloads will never hit the method
> and hence will never have a scalability problem.  Other workloads will
> hit the method but not heavily enough to be a concern.  A contrived
> workload which launches a bunch of threads and simply call this method
> in a tight loop will not scale.  So, the question really is: Is there
> a real workload out there that won't scale due to this method?
> 
> Nathan Reynolds | Consulting Member of Technical Staff | 602.333.9091
> Oracle PSR Engineering | Server Technology
> 
> On 1/8/2012 4:40 AM, Peter Firmstone wrote: 
> > Appended is a new java.security.Policy implementation, it fully supports
> > the existing java policy syntax and accepts alternate PolicyParser's. 
> > 
> > All state is immutable, except for 2 volatile references, referents
> > replaced, not mutated, when the policy is updated.  One referent is an
> > array containing PermissionGrant's (interface for immutable object
> > representing a grant statement in a policy), the second a
> > PermissionCollection containing the Policy Permissions.  The array is
> > never mutated after creation, a reference to the array is copied before
> > accessing the array or any array methods. 
> > 
> > The policy creates PermissionCollection's on demand for checking,
> > Permission's are ordered using a PermissionComparator to ensure that for
> > example, wildcard SocketPermission's are checked first, to avoid
> > unnecessary DNS lookups.  Only the permission being checked and any
> > UnresolvedPermission's are added to the PermissionCollection, limiting
> > the size of the objects created. 
> > 
> > In existing policy implementations PermissionCollection's perform
> > blocking operations. 
> > 
> > Also, after parsing policy files, PermissionGrant implementations avoid
> > the need to open files or network connections to confirm URL's, eg
> > CodeSource.implies is not called, but instead reimplemented using URI. 
> > 
> > Will this scale?  There but one smell: 
> > 
> > ProtectionDomain uses a synchronized method Policy.getPolicyNoCheck(),
> > but this only retrieves a reference on 99% of occasions. 
> > 
> > For every permission check, the stack access control context is
> > retrieved, every ProtectionDomain on the stack must be checked,
> > ProtectionDomain's must call getPolicyNoCheck() to call
> > Policy.implies(ProtectionDomain domain, Permission permission). 
> > 
> > To make this worse, I've got a SecurityManager that divides the
> > ProtectionDomain.implies() calls into tasks and submits them to an
> > executor (if there are 4 or more PD's in a context).  The
> > SecurityManager is also non blocking, at least it will be when I use the
> > new ConcurrentHashMap for the checked permission cache (avoids repeated
> > security checks), for now the cache is implemented using the existing
> > ConcurrentHashMap, but is mostly read in any case.  (P.S. This is the
> > cache I'm using the Reference Collection's for.) 
> > 
> > How much can this one synchronized method spoil scalability? 
> > 
> > Cheers & thanks in advance, 
> > 
> > Peter. 
> > 
> > 
> > 
> > /* 
> > *  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. 
> > */ 
> > 
> > /** 
> >  * Default Policy implementation taken from Apache Harmony, refactored
> > for 
> >  * concurrency. 
> >  * 
> >  * @author Alexey V. Varlamov 
> >  * @author Peter Firmstone 
> >  * @version $Revision$ 
> >  */ 
> > 
> > package net.jini.security.policy; 
> > 
> > import java.io.File; 
> > import java.net.URL; 
> > import java.security.AccessController; 
> > import java.security.AllPermission; 
> > import java.security.CodeSource; 
> > import java.security.Guard; 
> > import java.security.Permission; 
> > import java.security.PermissionCollection; 
> > import java.security.Permissions; 
> > import java.security.Policy; 
> > import java.security.PrivilegedActionException; 
> > import java.security.PrivilegedExceptionAction; 
> > import java.security.ProtectionDomain; 
> > import java.security.SecurityPermission; 
> > import java.security.UnresolvedPermission; 
> > import java.util.ArrayList; 
> > import java.util.Collection; 
> > import java.util.Enumeration; 
> > import java.util.Iterator; 
> > import java.util.List; 
> > import java.util.NavigableSet; 
> > import java.util.Properties; 
> > import java.util.TreeSet; 
> > import net.jini.security.PermissionComparator; 
> > import org.apache.river.api.security.PermissionGrant; 
> > import org.apache.river.impl.security.policy.util.DefaultPolicyParser; 
> > import org.apache.river.impl.security.policy.util.PolicyParser; 
> > import org.apache.river.impl.security.policy.util.PolicyUtils; 
> > 
> > 
> > /** 
> > * Concurrent Policy implementation based on policy configuration files, 
> > * it is intended to provide concurrent implies() for greatly improved 
> > * throughput at the expense of single threaded performance. 
> > * 
> > * Set the following system properties to use this Policy instead of the 
> > * built in Java sun.security.provider.PolicyFile: 
> > * * net.jini.security.policy.PolicyFileProvider.basePolicyClass = 
> > * org.apache.river.security.concurrent.ConcurrentPolicyFile 
> > * 
> > * 
> > * This 
> > * implementation recognizes text files, consisting of clauses with the 
> > * following syntax: 
> > * 
> > * <pre> 
> > * keystore &quot;some_keystore_url&quot; [, &quot;keystore_type&quot;]; 
> > * </pre> 
> > <pre> 
> > * grant [SignedBy &quot;signer_names&quot;] [, CodeBase &quot;URL&quot;]
> > *  [, Principal [principal_class_name] &quot;principal_name&quot;] 
> > *  [, Principal [principal_class_name] &quot;principal_name&quot;] ... {
> > *  permission permission_class_name [ &quot;target_name&quot; ] [,
> > &quot;action&quot;] 
> > *  [, SignedBy &quot;signer_names&quot;]; 
> > *  permission ... 
> > *  }; 
> > * * </pre> 
> > * 
> > * The <i>keystore </i> clause specifies reference to a keystore, which
> > is a 
> > * database of private keys and their associated digital certificates.
> > The 
> > * keystore is used to look up the certificates of signers specified in
> > the 
> > * <i>grant </i> entries of the file. The policy file can contain any
> > number of 
> > * <i>keystore </i> entries which can appear at any ordinal position.
> > However, 
> > * only the first successfully loaded keystore is used, others are
> > ignored. The 
> > * keystore must be specified if some grant clause refers to a
> > certificate's 
> > * alias. <br> 
> > * The <i>grant </i> clause associates a CodeSource (consisting of an URL
> > and a 
> > * set of certificates) of some executable code with a set of Permissions
> > which 
> > * should be granted to the code. So, the CodeSource is defined by values
> > of 
> > * <i>CodeBase </i> and <i>SignedBy </i> fields. The <i>CodeBase </i>
> > value must 
> > * be in URL format, while <i>SignedBy </i> value is a (comma-separated
> > list of) 
> > * alias(es) to keystore certificates. These fields can be omitted to
> > denote any 
> > * codebase and any signers (including case of unsigned code),
> > respectively. 
> > * <br> 
> > * Also, the code may be required to be executed on behalf of some
> > Principals 
> > * (in other words, code's ProtectionDomain must have the array of
> > Principals 
> > * associated) in order to possess the Permissions. This fact is
> > indicated by 
> > * specifying one or more <i>Principal </i> fields in the <i>grant </i>
> > clause. 
> > * Each Principal is specified as class/name pair; name and class can be
> > either 
> > * concrete value or wildcard <i>* </i>. As a special case, the class
> > value may 
> > * be omitted and then the name is treated as an alias to X.509
> > Certificate, and 
> > * the Principal is assumed to be javax.security.auth.x500.X500Principal
> > with a 
> > * name of subject's distinguished name from the certificate. <br> 
> > * The order between the <i>CodeBase </i>, <i>SignedBy </i>, and
> > <i>Principal 
> > * </i> fields does not matter. The policy file can contain any number of
> > grant 
> > * clauses. <br> 
> > * Each <i>grant </i> clause must contain one or more <i>permission </i>
> > entry. 
> > * The permission entry consist of a fully qualified class name along
> > with 
> > * optional <i>name </i>, <i>actions </i> and <i>signedby </i> values.
> > Name and 
> > * actions are arguments to the corresponding constructor of the
> > permission 
> > * class. SignedBy value represents the keystore alias(es) to
> > certificate(s) 
> > * used to sign the permission class. That is, this permission entry is 
> > * effective (i.e., access control permission will be granted based on
> > this 
> > * entry) only if the bytecode implementation of permission class is
> > verified to 
> > * be correctly signed by the said alias(es). <br> 
> > * <br> 
> > * The policy content may be parameterized via property expansion.
> > Namely, 
> > * expressions like <i>${key} </i> are replaced by values of
> > corresponding 
> > * system properties. Also, the special <i>slash </i> key (i.e. ${/}) is 
> > * supported, it is a shortcut to &quot;file.separator&quot; key.
> > Property 
> > * expansion is performed anywhere a double quoted string is allowed in
> > the 
> > * policy file. However, this feature is controlled by security
> > properties and 
> > * should be turned on by setting &quot;policy.expandProperties&quot;
> > property 
> > * to <i>true </i>. <br> 
> > * If property expansion fails (due to a missing key), a corresponding
> > entry is 
> > * ignored. For fields of <i>keystore </i> and <i>grant </i> clauses, the
> > whole 
> > * clause is ignored, and for <i>permission </i> entry, only that entry
> > is 
> > * ignored. <br> 
> > * <br> 
> > * The policy also supports generalized expansion in permissions names,
> > of 
> > * expressions like <i>${{protocol:data}} </i>. Currently the following 
> > * protocols supported: 
> > * <dl> 
> > * <dt>self 
> > * <dd>Denotes substitution to a principal information of the parental
> > Grant 
> > * entry. Replaced by a space-separated list of resolved Principals
> > (including 
> > * wildcarded), each formatted as <i>class &quot;name&quot; </i>. If
> > parental 
> > * Grant entry has no Principals, the permission is ignored. 
> > * <dt>alias: <i>name </i> 
> > * <dd>Denotes substitution of a KeyStore alias. Namely, if a KeyStore
> > has an 
> > * X.509 certificate associated with the specified name, then replaced by
> > * <i>javax.security.auth.x500.X500Principal &quot; <i>DN </i>&quot; </i>
> > * string, where <i>DN </i> is a certificate's subject distinguished
> > name. 
> > * </dl> 
> > * <br> 
> > * 
> > */ 
> > 
> > public class ConcurrentPolicyFile extends Policy implements
> > ConcurrentPolicy { 
> > 
> >    /** 
> >     * System property for dynamically added policy location. 
> >     */ 
> >    private static final String JAVA_SECURITY_POLICY =
> > "java.security.policy"; //$NON-NLS-1$ 
> > 
> >    /** 
> >     * Prefix for numbered Policy locations specified in
> > security.properties. 
> >     */ 
> >    private static final String POLICY_URL_PREFIX = "policy.url.";
> > //$NON-NLS-1$ 
> >      // Reference must be defensively copied before access, once
> > published, never mutated. 
> >    private volatile PermissionGrant [] grantArray; 
> >      // A specific parser for a particular policy file format. 
> >    private final PolicyParser parser; 
> >      private static final Guard guard = new
> > SecurityPermission("getPolicy"); 
> >      private final ProtectionDomain myDomain; 
> >      // reference must be defensively copied before access, once
> > published, never mutated. 
> >    private volatile PermissionCollection myPermissions; 
> >      /** 
> >     * Default constructor, equivalent to 
> >     * <code>ConcurrentPolicyFile(new DefaultPolicyParser())</code>. 
> >     */ 
> >    public ConcurrentPolicyFile() throws PolicyInitializationException { 
> >        this(new DefaultPolicyParser()); 
> >    } 
> > 
> >    /** 
> >     * Extension constructor for plugging-in a custom parser. 
> >     * @param dpr 
> >     */ 
> >    protected ConcurrentPolicyFile(PolicyParser dpr) throws
> > PolicyInitializationException { 
> >        guard.checkGuard(null); 
> >        parser = dpr; 
> >        myDomain = this.getClass().getProtectionDomain(); 
> >        /* 
> >         * The bootstrap policy makes implies decisions until this
> > constructor 
> >         * has returned.  We don't need to lock. 
> >         */ 
> >        try { 
> >            // Bug 4911907, do we need to do anything more? 
> >            // The permissions for this domain must be retrieved before 
> >            // construction is complete and this policy takes over. 
> >            initialize(); // Instantiates myPermissions. 
> >        } catch (SecurityException e){ 
> >            throw e; 
> >        } catch (Exception e){ 
> >            throw new PolicyInitializationException("PolicyInitialization
> > failed", e); 
> >        } 
> >    } 
> >      private PermissionCollection convert(NavigableSet<Permission>
> > permissions){ 
> >        PermissionCollection pc = new Permissions(); 
> >        // The descending iterator is for SocketPermission. 
> >        Iterator<Permission> it = permissions.descendingIterator(); 
> >        while (it.hasNext()) { 
> >            pc.add(it.next()); 
> >        } 
> >        return pc; 
> >    } 
> > 
> >    /** 
> >     * Returns collection of permissions allowed for the domain 
> >     * according to the policy. The evaluated characteristics of the 
> >     * domain are it's codesource and principals; they are assumed 
> >     * to be <code>null</code> if the domain is <code>null</code>. 
> >     * 
> >     * Each PermissionCollection returned is a unique instance. 
> >     * 
> >     * @param pd ProtectionDomain 
> >     * @see ProtectionDomain 
> >     */ 
> >    @Override 
> >    public PermissionCollection getPermissions(ProtectionDomain pd) { 
> >        NavigableSet<Permission> perms = new TreeSet<Permission>(new
> > PermissionComparator()); 
> >        PermissionGrant [] grantRefCopy = grantArray; 
> >        int l = grantRefCopy.length; 
> >        for ( int j =0; j < l; j++ ){ 
> >            PermissionGrant ge = grantRefCopy[j]; 
> >            if (ge.implies(pd)){ 
> >                if (ge.isPrivileged()){// Don't stuff around finish early
> > if you can. 
> >                    PermissionCollection pc = new Permissions(); 
> >                    pc.add(new AllPermission()); 
> >                    return pc; 
> >                } 
> >                Collection<Permission> c = ge.getPermissions(); 
> >                Iterator<Permission> i = c.iterator(); 
> >                while (i.hasNext()){ 
> >                    Permission p = i.next(); 
> >                    perms.add(p); 
> >                } 
> >            } 
> >        } 
> >        // Don't forget to merge the static Permissions. 
> >        PermissionCollection staticPC = null; 
> >        if (pd != null) { 
> >            staticPC = pd.getPermissions(); 
> >            if (staticPC != null){ 
> >                Enumeration<Permission> e = staticPC.elements(); 
> >                while (e.hasMoreElements()){ 
> >                    Permission p = e.nextElement(); 
> >                    if (p instanceof AllPermission) { 
> >                        PermissionCollection pc = new Permissions(); 
> >                        pc.add(p); 
> >                        return pc; 
> >                    } 
> >                    perms.add(p); 
> >                } 
> >            } 
> >        } 
> >        return convert(perms); 
> >    } 
> > 
> >    /** 
> >     * Returns collection of permissions allowed for the codesource 
> >     * according to the policy. 
> >     * The evaluation assumes that current principals are undefined. 
> >     * 
> >     * This returns a java.security.Permissions collection, which allows 
> >     * ProtectionDomain to optimise for the AllPermission case, which
> > avoids 
> >     * unnecessarily consulting the policy. 
> >     * 
> >     * If constructed with the four argument constructor,
> > ProtectionDomain.implies 
> >     * first consults the Policy, then it's own internal Permissions
> > collection, 
> >     * unless it has AllPermission, in which case it returns true without
> >     * consulting the policy. 
> >     * 
> >     * @param cs CodeSource 
> >     * @see CodeSource 
> >     */ 
> >    @Override 
> >    public PermissionCollection getPermissions(CodeSource cs) { 
> >        if (cs == null) throw new NullPointerException("CodeSource cannot
> > be null"); 
> >        NavigableSet<Permission> perms = new TreeSet<Permission>(new
> > PermissionComparator()); 
> >        // for ProtectionDomain AllPermission optimisation. 
> >        PermissionGrant [] grantRefCopy = grantArray; 
> >        int l = grantRefCopy.length; 
> >        for ( int j =0; j < l; j++ ){ 
> >            PermissionGrant ge = grantRefCopy[j]; 
> >            if (ge.implies(cs, null)){ // No Principal's 
> >                if (ge.isPrivileged()){// Don't stuff around finish early
> > if you can. 
> >                    PermissionCollection pc = new Permissions(); 
> >                    pc.add(new AllPermission()); 
> >                    return pc; 
> >                } 
> >                Collection<Permission> c = ge.getPermissions(); 
> >                Iterator<Permission> i = c.iterator(); 
> >                while (i.hasNext()){ 
> >                    Permission p = i.next(); 
> >                    perms.add(p); 
> >                } 
> >            } 
> >        } 
> >        return convert(perms); 
> >    } 
> >      @Override 
> >    public boolean implies(ProtectionDomain domain, Permission
> > permission) { 
> >        if (permission == null) throw new
> > NullPointerException("permission not allowed to be null"); 
> >        if (domain == myDomain) { 
> >            PermissionCollection pc = myPermissions; 
> >            return pc.implies(permission); 
> >        } 
> >        Class klass = permission.getClass(); 
> >        // Need to have a list of Permission's we can sort if permission
> > is SocketPermission. 
> >        NavigableSet<Permission> perms = new TreeSet<Permission>(new
> > PermissionComparator()); 
> >        PermissionGrant [] grantRefCopy = grantArray; 
> >        int l = grantRefCopy.length; 
> >        for ( int j =0; j < l; j++ ){ 
> >            PermissionGrant ge = grantRefCopy[j]; 
> >            if (ge.implies(domain)){ 
> >                if (ge.isPrivileged()) return true; // Don't stuff around
> > finish early if you can. 
> >                Collection<Permission> c = ge.getPermissions(); 
> >                Iterator<Permission> i = c.iterator(); 
> >                while (i.hasNext()){ 
> >                    Permission p = i.next(); 
> >                    // Don't make it larger than necessary. 
> >                    if (klass.isInstance(permission) || permission
> > instanceof UnresolvedPermission){ 
> >                        perms.add(p); 
> >                    } 
> >                } 
> >            } 
> >        } 
> >        // Don't forget to merge the static Permissions. 
> >        PermissionCollection staticPC = null; 
> >        if (domain != null) { 
> >            staticPC =domain.getPermissions(); 
> >            if (staticPC != null){ 
> >                Enumeration<Permission> e = staticPC.elements(); 
> >                while (e.hasMoreElements()){ 
> >                    Permission p = e.nextElement(); 
> >                    // return early if possible. 
> >                    if (p instanceof AllPermission ) return true; 
> >                    // Don't make it larger than necessary, but don't
> > worry about duplicates either. 
> >                    if (klass.isInstance(permission) || permission
> > instanceof UnresolvedPermission){ 
> >                        perms.add(p); 
> >                    } 
> >                } 
> >            } 
> >        } 
> >        return convert(perms).implies(permission); 
> >    } 
> > 
> >    /** 
> >     * Gets fresh list of locations and tries to load all of them in
> > sequence; 
> >     * failed loads are ignored. After processing all locations, old
> > policy 
> >     * settings are discarded and new ones come into force. <br> 
> >     * 
> >     * @see PolicyUtils#getPolicyURLs(Properties, String, String) 
> >     */ 
> >    @Override 
> >    public void refresh() { 
> >        try { 
> >            initialize(); 
> >        } catch (Exception ex) { 
> >            System.err.println(ex); 
> >        } 
> >    } 
> >      private void initialize() throws Exception{ 
> >        try { 
> >            Collection<PermissionGrant> fresh =
> > AccessController.doPrivileged( 
> >                new
> > PrivilegedExceptionAction<Collection<PermissionGrant>>(){ 
> >                    public Collection<PermissionGrant> run() throws
> > SecurityException { 
> >                        Collection<PermissionGrant> fresh = new
> > ArrayList<PermissionGrant>(120); 
> >                        Properties system = System.getProperties(); 
> >                        system.setProperty("/", File.separator);
> > //$NON-NLS-1$ 
> >                        URL[] policyLocations =
> > PolicyUtils.getPolicyURLs(system, 
> >                                                         
> > JAVA_SECURITY_POLICY, 
> >                                                         
> > POLICY_URL_PREFIX); 
> >                        int l = policyLocations.length; 
> >                        for (int i = 0; i < l; i++) { 
> >                            //TODO debug log 
> > //                                System.err.println("Parsing policy
> > file: " + policyLocations[i]); 
> >                            try { 
> >                                Collection<PermissionGrant> pc = null; 
> >                                pc = parser.parse(policyLocations[i],
> > system); 
> >                                fresh.addAll(pc); 
> >                            } catch (Exception e){ 
> >                                // It's best to let a SecurityException
> > bubble up 
> >                                // in case there is a problem with our
> > policy configuration 
> >                                // or implementation. 
> >                                if ( e instanceof SecurityException ) { 
> >                                    e.printStackTrace(System.out); 
> >                                    throw (SecurityException) e; 
> >                                } 
> >                                // ignore. 
> >                            } 
> >                        } 
> >                        return fresh; 
> >                    } 
> >                } 
> >            ); 
> >            // Volatile reference, publish after mutation complete. 
> >            grantArray = fresh.toArray(new
> > PermissionGrant[fresh.size()]); 
> >            myPermissions = getPermissions(myDomain); 
> >        }catch (PrivilegedActionException e){ 
> >            Throwable t = e.getCause(); 
> >            if ( t instanceof Exception ) throw (Exception) t; 
> >            throw e; 
> >        } 
> >    } 
> > 
> >    public boolean isConcurrent() { 
> >        return true; 
> >    } 
> > 
> >    public PermissionGrant[] getPermissionGrants() { 
> >        PermissionGrant [] grants = grantArray; // copy volatile
> > reference target. 
> >        return grants.clone(); 
> >    } 
> >      public PermissionGrant[] getPermissionGrants(ProtectionDomain pd) {
> >        PermissionGrant [] grants = grantArray; // copy volatile
> > reference target. 
> >        int l = grants.length; 
> >        List<PermissionGrant> applicable = new
> > ArrayList<PermissionGrant>(l); // Always too large, never too small. 
> >        for (int i =0; i < l; i++){ 
> >            if (grants[i].implies(pd)){ 
> >                applicable.add(grants[i]); 
> >            } 
> >        } 
> >        return applicable.toArray(new
> > PermissionGrant[applicable.size()]); 
> >    } 
> > 
> > } 
> > 
> > _______________________________________________
> > Concurrency-interest mailing list
> > Concurrency-interest@cs.oswego.edu
> > http://cs.oswego.edu/mailman/listinfo/concurrency-interest