Author: peter_firmstone Date: Fri Dec 23 02:26:21 2011 New Revision: 1222530
URL: http://svn.apache.org/viewvc?rev=1222530&view=rev Log: River-323 Creation of URLGrant to replace CodeSourceGrant's, this avoids dynamic lookup and other Security Manager and policy refactorings. Added: river/jtsk/skunk/peterConcurrentPolicy/src/org/apache/river/api/security/URIGrant.java - copied, changed from r1187844, river/jtsk/skunk/peterConcurrentPolicy/src/org/apache/river/api/security/CodeSourceGrant.java Modified: river/jtsk/skunk/peterConcurrentPolicy/src/net/jini/security/DynamicPermissionCollection.java river/jtsk/skunk/peterConcurrentPolicy/src/net/jini/security/policy/DynamicPolicyProvider.java river/jtsk/skunk/peterConcurrentPolicy/src/org/apache/river/api/security/CodeSourceSetGrant.java river/jtsk/skunk/peterConcurrentPolicy/src/org/apache/river/api/security/DelegateCombinerSecurityManager.java river/jtsk/skunk/peterConcurrentPolicy/src/org/apache/river/api/security/PermissionGrantBuilder.java river/jtsk/skunk/peterConcurrentPolicy/src/org/apache/river/api/security/PermissionGrantBuilderImp.java river/jtsk/skunk/peterConcurrentPolicy/src/org/apache/river/api/security/ProtectionDomainGrant.java river/jtsk/skunk/peterConcurrentPolicy/test/src/org/apache/river/api/security/DelegateCombinerSecurityManagerTest.java Modified: river/jtsk/skunk/peterConcurrentPolicy/src/net/jini/security/DynamicPermissionCollection.java URL: http://svn.apache.org/viewvc/river/jtsk/skunk/peterConcurrentPolicy/src/net/jini/security/DynamicPermissionCollection.java?rev=1222530&r1=1222529&r2=1222530&view=diff ============================================================================== --- river/jtsk/skunk/peterConcurrentPolicy/src/net/jini/security/DynamicPermissionCollection.java (original) +++ river/jtsk/skunk/peterConcurrentPolicy/src/net/jini/security/DynamicPermissionCollection.java Fri Dec 23 02:26:21 2011 @@ -71,6 +71,8 @@ final class DynamicPermissionCollection @Override public boolean implies(Permission permission) { if ( ! cl.isInstance(permission)) return false; + Thread thread = Thread.currentThread(); + if (thread.isInterrupted()) return false; Permission [] p = perms.toArray(new Permission[0]); //perms.size() may change if (comp != null){ Arrays.sort(p, comp); Modified: river/jtsk/skunk/peterConcurrentPolicy/src/net/jini/security/policy/DynamicPolicyProvider.java URL: http://svn.apache.org/viewvc/river/jtsk/skunk/peterConcurrentPolicy/src/net/jini/security/policy/DynamicPolicyProvider.java?rev=1222530&r1=1222529&r2=1222530&view=diff ============================================================================== --- river/jtsk/skunk/peterConcurrentPolicy/src/net/jini/security/policy/DynamicPolicyProvider.java (original) +++ river/jtsk/skunk/peterConcurrentPolicy/src/net/jini/security/policy/DynamicPolicyProvider.java Fri Dec 23 02:26:21 2011 @@ -522,6 +522,8 @@ Put the policy providers and all referen if ( pc != null ) { if (pc.implies(permission)) return true; } + Thread thread = Thread.currentThread(); + if (thread.isInterrupted()) return false; /* Do not call implies on the base Policy, if * there are UnresolvedPermission's that are undergoing resolution * while another Permission within that collection is already @@ -570,6 +572,8 @@ Put the policy providers and all referen // If the base policy doesn't imply a Permission then we should check for dynamic grants Collection<Permission> dynamicallyGrantedPermissions = new HashSet<Permission>(120); PermissionGrant[] grantsRefCopy = remotePolicyGrants; // In case the grants volatile reference is updated. + + if (thread.isInterrupted()) return false; int l = grantsRefCopy.length; for ( int i = 0; i < l; i++){ if (grantsRefCopy[i].implies(domain)) { @@ -578,6 +582,7 @@ Put the policy providers and all referen dynamicallyGrantedPermissions.addAll(Arrays.asList(perms)); } } + if (thread.isInterrupted()) return false; Iterator<PermissionGrant> grants = dynamicPolicyGrants.iterator(); while (grants.hasNext()){ PermissionGrant g = grants.next(); @@ -585,6 +590,7 @@ Put the policy providers and all referen dynamicallyGrantedPermissions.addAll(g.getPermissions()); } } + if (thread.isInterrupted()) return false; // if (loggable) { // logger.log(Level.FINEST, "Grants: " + dynamicallyGrantedPermissions.toString()); // } @@ -596,6 +602,7 @@ Put the policy providers and all referen while (dgpi.hasNext()){ pc.add(dgpi.next()); } + if (thread.isInterrupted()) return false; // If we get refreshed the cache could be empty, which is more pedantic // however the result may still be true so we'll return it anyway. // if (loggable) { Modified: river/jtsk/skunk/peterConcurrentPolicy/src/org/apache/river/api/security/CodeSourceSetGrant.java URL: http://svn.apache.org/viewvc/river/jtsk/skunk/peterConcurrentPolicy/src/org/apache/river/api/security/CodeSourceSetGrant.java?rev=1222530&r1=1222529&r2=1222530&view=diff ============================================================================== --- river/jtsk/skunk/peterConcurrentPolicy/src/org/apache/river/api/security/CodeSourceSetGrant.java (original) +++ river/jtsk/skunk/peterConcurrentPolicy/src/org/apache/river/api/security/CodeSourceSetGrant.java Fri Dec 23 02:26:21 2011 @@ -23,6 +23,8 @@ import java.io.ObjectInputStream; import java.security.CodeSource; import java.security.Permission; import java.security.Principal; +import java.util.ArrayList; +import java.util.Collection; import java.util.Collections; import java.util.HashSet; import java.util.Iterator; @@ -34,21 +36,21 @@ import java.util.Set; */ class CodeSourceSetGrant extends CertificateGrant { private static final long serialVersionUID = 1L; - private final Set<CodeSource> cs; + private final Collection<CodeSource> cs; private final int hashCode; @SuppressWarnings("unchecked") CodeSourceSetGrant(CodeSource[] csource, Principal[] pals, Permission[] perm, boolean inverse){ super( null, pals, perm, inverse); int l = csource == null ? 0 : csource.length; - Set<CodeSource> set = new HashSet<CodeSource>(l); + Collection<CodeSource> list = new ArrayList<CodeSource>(l); int hash = 3; for (int i = 0 ; i < l ; i++ ){ if ( csource[i] == null) throw new NullPointerException("CodeSource array must not contain null values"); - set.add(normalizeCodeSource(csource[i])); + list.add(normalizeCodeSource(csource[i])); } - cs = Collections.unmodifiableSet(set); + cs = Collections.unmodifiableCollection(list); hash = 67 * hash + (this.cs != null ? this.cs.hashCode() : 0); hash = 67 * hash + (super.hashCode()); hashCode = hash; Modified: river/jtsk/skunk/peterConcurrentPolicy/src/org/apache/river/api/security/DelegateCombinerSecurityManager.java URL: http://svn.apache.org/viewvc/river/jtsk/skunk/peterConcurrentPolicy/src/org/apache/river/api/security/DelegateCombinerSecurityManager.java?rev=1222530&r1=1222529&r2=1222530&view=diff ============================================================================== --- river/jtsk/skunk/peterConcurrentPolicy/src/org/apache/river/api/security/DelegateCombinerSecurityManager.java (original) +++ river/jtsk/skunk/peterConcurrentPolicy/src/org/apache/river/api/security/DelegateCombinerSecurityManager.java Fri Dec 23 02:26:21 2011 @@ -40,6 +40,7 @@ import java.util.concurrent.ExecutionExc import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; +import java.util.concurrent.atomic.AtomicBoolean; import java.util.logging.Level; import java.util.logging.Logger; import org.apache.river.api.delegates.DelegatePermission; @@ -94,8 +95,9 @@ extends SecurityManager implements Deleg // Checks if Permission has already been checked for this context. Set<Permission> checkedPerms = checked.get(executionContext); if (checkedPerms == null){ - Set<Referrer<Permission>> internal = new HashSet<Referrer<Permission>>(96); - checkedPerms = CollectionsConcurrent.multiReadSet(RC.set(internal, Ref.SOFT)); + Set<Referrer<Permission>> internal = + CollectionsConcurrent.multiReadSet(new HashSet<Referrer<Permission>>(96)); + checkedPerms = RC.set(internal, Ref.SOFT); Set<Permission> existed = checked.putIfAbsent(executionContext, checkedPerms); if (existed != null) checkedPerms = existed; } @@ -228,8 +230,9 @@ extends SecurityManager implements Deleg CountDownLatch latch = new CountDownLatch(l); List<Future<Boolean>> resultList = null; Collection<Callable<Boolean>> tasks = new ArrayList<Callable<Boolean>>(l); + AtomicBoolean terminated = new AtomicBoolean(false); for ( int i = 0; i < l; i++ ){ - tasks.add(new PermissionCheck(context[i], perm, latch, currentThread)); + tasks.add(new PermissionCheck(context[i], perm, latch, currentThread, terminated)); } try { // We can change either call to add a timeout. @@ -249,15 +252,26 @@ extends SecurityManager implements Deleg Logger.getLogger(DelegateCombinerSecurityManager.class.getName()).log(Level.SEVERE, null, ex); } } catch (InterruptedException ex) { - // This is normal, it just means one task returned false. - // Swallow the interrupt, because it will likely be caused by - // a negative result, in which case a SecurityException will - // be thrown anyway. + // A task could return false, after the interruption and be + // interleaved before this check, it isn't foolproof. This + // cannot be worked around since the exception clears the interrupt + // status. + // In that case the SecurityException will be thrown and the + // interrupt status swallowed. + boolean externalInterrupt = false; + if (!terminated.get()) externalInterrupt = true; + Iterator<Future<Boolean>> it = resultList.iterator(); while (it.hasNext()){ it.next().cancel(true); } Logger.getLogger(DelegateCombinerSecurityManager.class.getName()).log(Level.FINEST, null, ex); + // Task interruption is normal, it just means one task returned false. + // Swallow the interrupt, unless externally interrupted. + if (externalInterrupt){ + // Restore external interrupt. + currentThread.interrupt(); + } } return false; } @@ -288,29 +302,33 @@ extends SecurityManager implements Deleg private final Permission p; private final CountDownLatch latch; private final Thread caller; + private final AtomicBoolean terminated; - PermissionCheck(ProtectionDomain pd, Permission p, CountDownLatch c, Thread caller){ + PermissionCheck(ProtectionDomain pd, Permission p, CountDownLatch c, Thread caller, AtomicBoolean terminated){ if (pd == null || p == null) throw new NullPointerException(); this.pd = pd; this.p = p; latch = c; this.caller = caller; + this.terminated = terminated; } public Boolean call() throws Exception { - Boolean result = Boolean.FALSE; - if (pd.implies(p)) result = Boolean.TRUE; - if (p instanceof DelegatePermission ){ + boolean result = false; + result = pd.implies(p); + if (result == false && p instanceof DelegatePermission ){ Permission candidate = ((DelegatePermission)p).getPermission(); - if (pd.implies(candidate)){ - result = Boolean.TRUE; - } + result = pd.implies(candidate); } // If we need to check for any Permission that we have substituted // for a standard jvm permission, getPermissions and convert, // then test here for static ProtectionDomain's. - if (Boolean.FALSE.equals(result)) { - caller.interrupt(); + if ( result == false ) { + // This only allows one task to terminate the caller, + // preventing late terminating tasks from resetting the interrupt. + // The first task to return false initiates early termination + // of the other tasks. + if (terminated.compareAndSet(false, true)) caller.interrupt(); } latch.countDown(); return result; Modified: river/jtsk/skunk/peterConcurrentPolicy/src/org/apache/river/api/security/PermissionGrantBuilder.java URL: http://svn.apache.org/viewvc/river/jtsk/skunk/peterConcurrentPolicy/src/org/apache/river/api/security/PermissionGrantBuilder.java?rev=1222530&r1=1222529&r2=1222530&view=diff ============================================================================== --- river/jtsk/skunk/peterConcurrentPolicy/src/org/apache/river/api/security/PermissionGrantBuilder.java (original) +++ river/jtsk/skunk/peterConcurrentPolicy/src/org/apache/river/api/security/PermissionGrantBuilder.java Fri Dec 23 02:26:21 2011 @@ -18,6 +18,8 @@ package org.apache.river.api.security; +import java.net.URI; +import java.net.URISyntaxException; import java.security.CodeSource; import java.security.Permission; import java.security.Principal; @@ -77,6 +79,8 @@ public abstract class PermissionGrantBui */ public static final int PRINCIPAL = 4; + public static final int URI = 5; + public static PermissionGrantBuilder newBuilder(){ return new PermissionGrantBuilderImp(); } @@ -113,13 +117,17 @@ public abstract class PermissionGrantBui public abstract PermissionGrantBuilder codeSource(CodeSource cs); public abstract PermissionGrantBuilder multipleCodeSources(); + + public abstract PermissionGrantBuilder uri(URI uri); /** - * Extracts the CodeSource, Certificates, ClassLoader and ProtectionDomain + * Extracts ProtectionDomain * from the Class for use in the PermissionGrantBuilder. The ClassLoader * and ProtectionDomain are weakly referenced, when collected any * created PermissionGrant affected will be voided. * @param cl * @return PermissionGrantBuilder. + * @throws URISyntaxException - this exception may be swallowed if a URI + * grant is not required. */ public abstract PermissionGrantBuilder clazz(Class cl); /** Modified: river/jtsk/skunk/peterConcurrentPolicy/src/org/apache/river/api/security/PermissionGrantBuilderImp.java URL: http://svn.apache.org/viewvc/river/jtsk/skunk/peterConcurrentPolicy/src/org/apache/river/api/security/PermissionGrantBuilderImp.java?rev=1222530&r1=1222529&r2=1222530&view=diff ============================================================================== --- river/jtsk/skunk/peterConcurrentPolicy/src/org/apache/river/api/security/PermissionGrantBuilderImp.java (original) +++ river/jtsk/skunk/peterConcurrentPolicy/src/org/apache/river/api/security/PermissionGrantBuilderImp.java Fri Dec 23 02:26:21 2011 @@ -22,15 +22,20 @@ import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; +import java.net.URISyntaxException; import java.util.Collection; import java.lang.ref.WeakReference; +import java.net.URI; import java.security.CodeSource; import java.security.Permission; import java.security.Principal; import java.security.ProtectionDomain; import java.security.cert.Certificate; +import java.util.ArrayList; import java.util.Collections; import java.util.HashSet; +import java.util.logging.Level; +import java.util.logging.Logger; /** * PermissionGrantBuilderImp represents the serialized form of all @@ -50,6 +55,7 @@ class PermissionGrantBuilderImp extends private static final PermissionGrant nullGrant = new NullPermissionGrant(); // Serial Form + private URI[] uri; private CodeSource cs; private CodeSource[] csources; private Certificate[] certs; @@ -62,6 +68,7 @@ class PermissionGrantBuilderImp extends // Transient Fields private transient Collection<CodeSource> multipleCodeSources; + private transient Collection<URI> uris; private transient WeakReference<ProtectionDomain> domain; PermissionGrantBuilderImp() { @@ -73,7 +80,9 @@ class PermissionGrantBuilderImp extends * Resets builder back to initial state, ready to receive new information * for building a new PermissionGrant. */ - public PermissionGrantBuilder reset() { + public final PermissionGrantBuilder reset() { + uri = null; + if (uris != null) uris.clear(); cs = null; certs = null; domain = null; @@ -106,6 +115,13 @@ class PermissionGrantBuilderImp extends this.context = context; return this; } + + @Override + public PermissionGrantBuilder uri(java.net.URI uri) { + if (this.uris == null) this.uris = new ArrayList<URI>(6); + this.uris.add(uri); + return this; + } public PermissionGrantBuilder codeSource(CodeSource cs) { if (hasMultipleCodeSources){ @@ -132,18 +148,6 @@ class PermissionGrantBuilderImp extends if ( pd != null ){ domain = new WeakReference<ProtectionDomain>(pd); hasDomain = true; - CodeSource cdsrc = pd.getCodeSource(); - if (cs == null) { - cs = cdsrc; - } - if (certs == null && cdsrc != null) { - certs = cdsrc.getCertificates(); - } - // No class should ever have any Principal's in it's ProtectionDomain - // Only a DomainCombiner should add principals. -// if (principals == null) { -// principals = pd.getPrincipals(); -// } } } return this; @@ -178,6 +182,10 @@ class PermissionGrantBuilderImp extends // are treated special. if (inverse) throw new UnsupportedOperationException("Inverse ClassLoader permissions not implemented"); return new ClassLoaderGrant(domain, principals, permissions ); + case URI: + if (uris != null && !uris.isEmpty() ) uri = uris.toArray(new URI[uris.size()]); + if (uri == null ) uri = new URI[0]; + return new URIGrant(uri, certs, principals, permissions, inverse); case CODESOURCE: if (hasMultipleCodeSources) { if (multipleCodeSources != null) csources = @@ -216,6 +224,7 @@ class PermissionGrantBuilderImp extends multipleCodeSources.toArray(new CodeSource[multipleCodeSources.size()]); cs = null; } + if (uris != null && !uris.isEmpty()) uri = uris.toArray(new URI[uris.size()]); out.defaultWriteObject(); } Modified: river/jtsk/skunk/peterConcurrentPolicy/src/org/apache/river/api/security/ProtectionDomainGrant.java URL: http://svn.apache.org/viewvc/river/jtsk/skunk/peterConcurrentPolicy/src/org/apache/river/api/security/ProtectionDomainGrant.java?rev=1222530&r1=1222529&r2=1222530&view=diff ============================================================================== --- river/jtsk/skunk/peterConcurrentPolicy/src/org/apache/river/api/security/ProtectionDomainGrant.java (original) +++ river/jtsk/skunk/peterConcurrentPolicy/src/org/apache/river/api/security/ProtectionDomainGrant.java Fri Dec 23 02:26:21 2011 @@ -21,10 +21,17 @@ package org.apache.river.api.security; import java.io.InvalidObjectException; import java.io.ObjectInputStream; import java.lang.ref.WeakReference; +import java.net.URI; +import java.net.URISyntaxException; +import java.net.URL; import java.security.CodeSource; import java.security.Permission; import java.security.Principal; import java.security.ProtectionDomain; +import java.security.cert.Certificate; +import java.util.Arrays; +import java.util.logging.Level; +import java.util.logging.Logger; /** * ProtectionDomainGrant's become void if serialized, since ProtectionDomain's @@ -115,7 +122,8 @@ class ProtectionDomainGrant extends Prin if (pd == null) return false; if (domain.get() == null ) return false; // grant is void. if ( pd.equals(domain.get())) return true; // pd not null fast reference comparison - if ( impliesClassLoader(pd.getClassLoader()) && impliesCodeSource(pd.getCodeSource())) + if ( impliesClassLoader(pd.getClassLoader()) + && impliesCodeSource(pd.getCodeSource())) { return true; } @@ -139,11 +147,31 @@ class ProtectionDomainGrant extends Prin private boolean impliesCodeSource(CodeSource codeSource) { ProtectionDomain pd = domain != null ? domain.get(): null; if (pd == null) return false; // is void - why did I have true? - CodeSource cs = normalizeCodeSource(pd.getCodeSource()); + // The CodeSource is not normalized, if the PD was created by a domain + // combiner it is likely that the CodeSource will have the same referent. + CodeSource cs = pd.getCodeSource(); // Don't normalise CodeSource. if (cs == codeSource) return true; // same reference. - if (cs == null && codeSource == null) return true; // if both null, whe pd exists. + if (cs == null && codeSource == null) return true; // if both null, when pd exists. if (cs == null) return false; // Null cs indicates system domain, does not imply. - return cs.implies(normalizeCodeSource(codeSource)); + // Most won't get to here, since domain combiners shouldn't duplicate CodeSource. + // But just in case... + // Don't use CodeSource.equals() because that causes DNS Lookup. + Certificate[] myCerts = cs.getCertificates(); + Certificate[] hisCerts = codeSource.getCertificates(); + if ( myCerts != null && !Arrays.equals(myCerts, hisCerts)) return false; + try { + URI myLocation = cs.getLocation().toURI(); + URI hisLocation = codeSource.getLocation().toURI(); + if (myLocation.equals(hisLocation)) return true; + } catch (URISyntaxException ex) { + // We only compare URL if we can't compare URI + URL myLocation = cs.getLocation(); + URL hisLocation = codeSource.getLocation(); + // Only use string representation to compare, DNS cache poisioning + // presents a security risk, so URL.equals isn't used. + if (myLocation.toExternalForm().equals(hisLocation.toExternalForm())) return true; + } + return false; } @Override Copied: river/jtsk/skunk/peterConcurrentPolicy/src/org/apache/river/api/security/URIGrant.java (from r1187844, river/jtsk/skunk/peterConcurrentPolicy/src/org/apache/river/api/security/CodeSourceGrant.java) URL: http://svn.apache.org/viewvc/river/jtsk/skunk/peterConcurrentPolicy/src/org/apache/river/api/security/URIGrant.java?p2=river/jtsk/skunk/peterConcurrentPolicy/src/org/apache/river/api/security/URIGrant.java&p1=river/jtsk/skunk/peterConcurrentPolicy/src/org/apache/river/api/security/CodeSourceGrant.java&r1=1187844&r2=1222530&rev=1222530&view=diff ============================================================================== --- river/jtsk/skunk/peterConcurrentPolicy/src/org/apache/river/api/security/CodeSourceGrant.java (original) +++ river/jtsk/skunk/peterConcurrentPolicy/src/org/apache/river/api/security/URIGrant.java Fri Dec 23 02:26:21 2011 @@ -20,25 +20,38 @@ package org.apache.river.api.security; import java.io.InvalidObjectException; import java.io.ObjectInputStream; +import java.net.URI; +import java.net.URISyntaxException; +import java.net.URL; import java.security.CodeSource; import java.security.Permission; import java.security.Principal; +import java.security.cert.Certificate; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Iterator; /** * * @author Peter Firmstone */ -class CodeSourceGrant extends CertificateGrant { +class URIGrant extends CertificateGrant { private static final long serialVersionUID = 1L; - private final CodeSource cs; + private final Collection<URI> location; private final int hashCode; @SuppressWarnings("unchecked") - CodeSourceGrant(CodeSource cs, Principal[] pals, Permission[] perm, boolean inverse ){ - super( cs != null? cs.getCertificates(): null, pals, perm, inverse); - this.cs = cs != null? normalizeCodeSource(cs) : null; + URIGrant(URI[] uri, Certificate[] certs, Principal[] pals, Permission[] perm, boolean inverse ){ + super( certs, pals, perm, inverse); + int l = uri.length; + Collection<URI> uris = new ArrayList<URI>(l); + for ( int i = 0; i < l ; i++ ){ + uris.add(uri[i] != null ? uri[i].normalize() : null); + } + location = Collections.unmodifiableCollection(uris); int hash = 3; - hash = 67 * hash + (this.cs != null ? this.cs.hashCode() : 0); + hash = 67 * hash + (this.location != null ? uri.hashCode() : 0); hash = 67 * hash + (super.hashCode()); hashCode = hash; } @@ -53,13 +66,11 @@ class CodeSourceGrant extends Certificat if (o == null) return false; if (o == this) return true; if (o.hashCode() != this.hashCode()) return false; - if (o instanceof CodeSourceGrant){ - CodeSourceGrant c = (CodeSourceGrant) o; + if (o instanceof URIGrant){ + URIGrant c = (URIGrant) o; if ( !super.equals(o)) return false; - if ( cs == c.cs) return true; - if ( cs != null ) { - if (cs.equals(c.cs)) return true; - } + if ( location == c.location) return true; + if ( location.equals(c.location)) return true; } return false; } @@ -68,40 +79,321 @@ class CodeSourceGrant extends Certificat public String toString(){ StringBuilder sb = new StringBuilder(500); return sb.append(super.toString()) - .append("CodeSource: \n") - .append( cs == null ? "null" : cs.toString()) + .append("URI: \n") + .append(location.toString()) .toString(); } @Override public boolean implies(ClassLoader cl, Principal[] p){ if ( !implies(p)) return false; - if ( cs == null ) return true; + if ( location.isEmpty() ) return true; + Iterator<URI> it = location.iterator(); + while (it.hasNext()){ + if (it.next() == null) return true; + } if ( cl == null ) return false; return false; //Indeterminate. } /** - * Checks if passed CodeSource matches this PermissionGrant. Null CodeSource of - * PermissionGrant implies any CodeSource; non-null CodeSource forwards to its - * imply() method. + * Checks if passed CodeSource matches this PermissionGrant. Null URI of + * PermissionGrant implies any CodeSource, only if that CodeSource is not + * null. */ @Override public boolean implies(CodeSource codeSource, Principal[] p) { if ( !implies(p)) return false; - // sun.security.provider.PolicyFile compatibility for null CodeSource. + // sun.security.provider.PolicyFile compatibility for null CodeSource is false. // see com.sun.jini.test.spec.policyprovider.dynamicPolicyProvider.GrantPrincipal test. - if ( codeSource == null ) return false; - if ( cs == null || nullCS.equals(cs)) return true; - return cs.implies(normalizeCodeSource(codeSource)); + if (codeSource == null) return false; + if (location.isEmpty()) return true; + int l = location.size(); + URI[] uris = location.toArray(new URI[l]); + for (int i = 0; i<l ; i++ ){ + if (uris[i] == null) return true; + } + for (int i = 0; i<l ; i++){ + if (implies(uris[i], codeSource)) return true; } + return false; + } + + /* This section of code was copied from Apache Harmony's CodeSource + * SVN Revision 929252 + * + * + * Indicates whether the specified code source is implied by this {@code + * URI}. Returns {@code true} if all of the following conditions are + * {@code true}, otherwise {@code false}: + * <p> + * <ul> + * <li>{@code cs} is not {@code null} + * <li>if this {@code CodeSource}'s location is not {@code null}, the + * following conditions are checked + * <ul> + * <li>this {@code CodeSource}'s location is not {@code null} + * <li>this {@code CodeSource}'s location protocol is equal to {@code cs}'s + * location protocol + * <li>if this {@code CodeSource}'s location host is not {@code null}, the + * following conditions are checked + * <ul> + * <li>{@code cs}'s host is not {@code null} + * <li>the wildcard or partial wildcard of this {@code URI}'s + * host matches {@code cs}'s location host. + * </ul> + * <li>if this {@code CodeSource}'s location port != -1 the port of {@code + * cs}'s location is equal to this {@code CodeSource}'s location port + * <li>this {@code CodeSource}'s location file matches {@code cs}'s file + * whereas special wildcard matching applies as described below + * <li>this {@code CodeSource}'s location reference is equal to to {@code + * cs}'s location reference + * </ul> + * </ul> + * <p> + * Note: If this {@code CodeSource} has a {@code null} location, + * this method returns {@code true}. + * <p> + * Matching rules for the {@code CodeSource}'s location file: + * <ul> + * <li>if this {@code CodeSource}'s location file ends with {@code "/-"}, + * then {@code cs}'s file must start with {@code CodeSource}'s location file + * (exclusive the trailing '-') + * <li>if this {@code CodeSource}'s location file ends with {@code "/*"}, + * then {@code cs}'s file must start with {@code CodeSource}'s location file + * (exclusive the trailing '*') and must not have any further '/' + * <li>if this {@code CodeSource}'s location file ends with {@code "/"}, + * then {@code cs}'s file must start with {@code CodeSource}'s location file + * <li>if this {@code CodeSource}'s location file does not end with {@code + * "/"}, then {@code cs}'s file must start with {@code CodeSource}'s + * location file with the '/' appended to it. + * </ul> + * Examples for locations that imply the location + * "http://harmony.apache.org/milestones/M9/apache-harmony.jar": + * + * <pre> + * http: + * http://*/milestones/M9/* + * http://*.apache.org/milestones/M9/* + * http://harmony.apache.org/milestones/- + * http://harmony.apache.org/milestones/M9/apache-harmony.jar + * </pre> + * + * @param cs + * the code source to check. + * @return {@code true} if the argument code source is implied by this + * {@code CodeSource}, otherwise {@code false}. + */ + private static boolean implies(URI location, CodeSource cs) { + // + // Here, javadoc:N refers to the appropriate item in the API spec for + // the CodeSource.implies() + // The info was taken from the 1.5 final API spec + + // javadoc:1 +// if (cs == null) { +// return false; +// } + + /* Certificates can safely be ignored, they're checked by CertificateGrant */ + + // javadoc:2 + // with a comment: the javadoc says only about certificates and does + // not explicitly mention CodeSigners' certs. + // It seems more convenient to use getCerts() to get the real + // certificates - with a certificates got form the signers +// Certificate[] thizCerts = getCertificatesNoClone(); +// if (thizCerts != null) { +// Certificate[] thatCerts = cs.getCertificatesNoClone(); +// if (thatCerts == null +// || !PolicyUtils.matchSubset(thizCerts, thatCerts)) { +// return false; +// } +// } + + // javadoc:3 + if (location != null) { + + //javadoc:3.1 + URL otherURL = cs.getLocation(); + if ( otherURL == null) { + return false; + } + URI otherURI; + try { + otherURI = otherURL.toURI(); + } catch (URISyntaxException ex) { + return false; + } + //javadoc:3.2 + if (location.equals(otherURI)) { + return true; + } + //javadoc:3.3 + if (!location.getSchemeSpecificPart().equals(otherURI.getSchemeSpecificPart())) { + return false; + } + //javadoc:3.4 + String thisHost = location.getHost(); + if (thisHost != null) { + String thatHost = otherURI.getHost(); + if (thatHost == null) { + return false; + } + + // 1. According to the spec, an empty string will be considered + // as "localhost" in the SocketPermission + // 2. 'file://' URLs will have an empty getHost() + // so, let's make a special processing of localhost-s, I do + // believe this'll improve performance of file:// code sources + + // + // Don't have to evaluate both the boolean-s each time. + // It's better to evaluate them directly under if() statement. + // + // boolean thisIsLocalHost = thisHost.length() == 0 || "localhost".equals(thisHost); + // boolean thatIsLocalHost = thatHost.length() == 0 || "localhost".equals(thatHost); + // + // if( !(thisIsLocalHost && thatIsLocalHost) && + // !thisHost.equals(thatHost)) { + + if (!((thisHost.length() == 0 || "localhost".equals(thisHost)) && (thatHost //$NON-NLS-1$ + .length() == 0 || "localhost".equals(thatHost))) //$NON-NLS-1$ + && !thisHost.equals(thatHost)) { + + // Do wildcard matching here to replace SocketPermission functionality. + // This section was copied from Apache Harmony SocketPermission + boolean hostNameMatches = false; + boolean isPartialWild = (thisHost.charAt(0) == '*'); + if (isPartialWild) { + boolean isWild = (thisHost.length() == 1); + if (isWild) { + hostNameMatches = true; + } else { + // Check if thisHost matches the end of thatHost after the wildcard + int length = thisHost.length() - 1; + hostNameMatches = thatHost.regionMatches(thatHost.length() - length, + thisHost, 1, length); + } + } + if (!hostNameMatches) return false; // else continue. + + /* Don't want to try resolving URIGrant, it either has a + * matching host or it doesn't. + * + * The following section is for resolving hosts, it is + * not relevant here, but has been preserved for information + * purposes only. + * + * Not only is it expensive to perform DNS resolution, hence + * the creation of URIGrant, but a CodeSource.implies + * may also require another SocketPermission which may + * cause the policy to get stuck in an endless loop, since it + * doesn't perform the implies in priviledged mode, it might + * also allow an attacker to substitute one codebase for + * another using a dns cache poisioning attack. In any case + * the DNS cannot be assumed trustworthy enough to supply + * the policy with information at this level. The implications + * are greater than the threat posed by SocketPermission + * which simply allows a network connection, as this may + * apply to any Permission, even AllPermission. + * + * Typically the URI of the codebase will be a match for + * the codebase annotation string that is stored as a URL + * in CodeSource, then converted to a URI for comparison. + */ + + // Obvious, but very slow way.... + // + // SocketPermission thisPerm = new SocketPermission( + // this.location.getHost(), "resolve"); + // SocketPermission thatPerm = new SocketPermission( + // cs.location.getHost(), "resolve"); + // if (!thisPerm.implies(thatPerm)) { + // return false; + // } + // + // let's cache it: + +// if (this.sp == null) { +// this.sp = new SocketPermission(thisHost, "resolve"); //$NON-NLS-1$ +// } +// +// if (cs.sp == null) { +// cs.sp = new SocketPermission(thatHost, "resolve"); //$NON-NLS-1$ +// } +// +// if (!this.sp.implies(cs.sp)) { +// return false; +// } + + } // if( ! this.location.getHost().equals(cs.location.getHost()) + } // if (this.location.getHost() != null) + + //javadoc:3.5 + if (location.getPort() != -1) { + if (location.getPort() != otherURI.getPort()) { + return false; + } + } + + //javadoc:3.6 + // for compatbility with URL.getFile, the Query is concatenated to the path. + String thisFile = location.getPath() + location.getQuery(); + String thatFile = otherURI.getPath() + otherURI.getQuery(); + + if (thisFile.endsWith("/-")) { //javadoc:3.6."/-" //$NON-NLS-1$ + if (!thatFile.startsWith(thisFile.substring(0, thisFile + .length() - 2))) { + return false; + } + } else if (thisFile.endsWith("/*")) { //javadoc:3.6."/*" //$NON-NLS-1$ + if (!thatFile.startsWith(thisFile.substring(0, thisFile + .length() - 2))) { + return false; + } + // no further separators(s) allowed + if (thatFile.indexOf("/", thisFile.length() - 1) != -1) { //$NON-NLS-1$ + return false; + } + } else { + // javadoc:3.6."/" + if (!thisFile.equals(thatFile)) { + if (!thisFile.endsWith("/")) { //$NON-NLS-1$ + if (!thatFile.equals(thisFile + "/")) { //$NON-NLS-1$ + return false; + } + } else { + return false; + } + } + } + + //javadoc:3.7 + // A URL Anchor is a URI Fragment. + if (location.getFragment() != null) { + if (!location.getFragment().equals(otherURI.getFragment())) { + return false; + } + } + // ok, every check was made, and they all were successful. + // it's ok to return true. + } // if this.location != null + + // javadoc: a note about CodeSource with null location and null Certs + // is applicable here + return true; + } @Override public PermissionGrantBuilder getBuilderTemplate() { PermissionGrantBuilder pgb = super.getBuilderTemplate(); - pgb.codeSource(cs) - .context(PermissionGrantBuilder.CODESOURCE); + Iterator<URI> it = location.iterator(); + while (it.hasNext()){ + pgb.uri(it.next()); + } + pgb.context(PermissionGrantBuilder.URI); return pgb; } Modified: river/jtsk/skunk/peterConcurrentPolicy/test/src/org/apache/river/api/security/DelegateCombinerSecurityManagerTest.java URL: http://svn.apache.org/viewvc/river/jtsk/skunk/peterConcurrentPolicy/test/src/org/apache/river/api/security/DelegateCombinerSecurityManagerTest.java?rev=1222530&r1=1222529&r2=1222530&view=diff ============================================================================== --- river/jtsk/skunk/peterConcurrentPolicy/test/src/org/apache/river/api/security/DelegateCombinerSecurityManagerTest.java (original) +++ river/jtsk/skunk/peterConcurrentPolicy/test/src/org/apache/river/api/security/DelegateCombinerSecurityManagerTest.java Fri Dec 23 02:26:21 2011 @@ -63,7 +63,7 @@ public class DelegateCombinerSecurityMan try { sm = new DelegateCombinerSecurityManager(); CodeSource cs0, cs10, cs11, cs12; - p1 = new SocketPermission("river.apache.org:80", "connect,accept"); + p1 = new SocketPermission("*", "connect,accept"); p2 = DelegatePermission.get(p1); p3 = new RuntimePermission("readFileDescriptor"); p4 = DelegatePermission.get(p3);
