Certificate and Principal support basically added in with a mess of jury rigging to get basic unit tests running and showing that things are functional. Need to refactor the Policy loading/Policy object interactions some more as I am not sure how reliable the creation of the ProtectionDomain is in the current state and I am not happy with the way the loader looks atm. First things first will be to further flesh out unit tests to validate correct funtionality in more scenarios.

git-svn-id: svn+ssh://dev.eclipse.org/svnroot/rt/org.eclipse.jetty/jetty/trunk@435 7e9141cc-0065-0410-87d8-b60c137991c4
This commit is contained in:
Jesse McConnell 2009-06-25 21:00:56 +00:00
parent be85a6a16d
commit 62ff473a50
8 changed files with 372 additions and 67 deletions

View File

@ -9,6 +9,10 @@
<artifactId>jetty-policy</artifactId>
<name>Jetty :: Policy Tool</name>
<packaging>jar</packaging>
<properties>
<jetty.test.policy>1.0-SNAPSHOT</jetty.test.policy>
<jetty.test.policy.loc>target/test-policy</jetty.test.policy.loc>
</properties>
<build>
<plugins>
<plugin>
@ -28,6 +32,52 @@
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<executions>
<execution>
<id>unpack</id>
<phase>generate-resources</phase>
<goals>
<goal>unpack</goal>
</goals>
<configuration>
<artifactItems>
<artifactItem>
<groupId>org.eclipse.jetty.toolchain</groupId>
<artifactId>jetty-test-policy</artifactId>
<version>${jetty.test.policy}</version>
<type>jar</type>
<overWrite>true</overWrite>
<includes>**/*.keystore</includes>
<outputDirectory>${jetty.test.policy.loc}</outputDirectory>
</artifactItem>
</artifactItems>
</configuration>
</execution>
<execution>
<id>copy</id>
<phase>generate-resources</phase>
<goals>
<goal>copy</goal>
</goals>
<configuration>
<artifactItems>
<artifactItem>
<groupId>org.eclipse.jetty.toolchain</groupId>
<artifactId>jetty-test-policy</artifactId>
<version>${jetty.test.policy}</version>
<type>jar</type>
<overWrite>true</overWrite>
<includes>**</includes>
<outputDirectory>${jetty.test.policy.loc}</outputDirectory>
</artifactItem>
</artifactItems>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
<dependencies>
@ -36,18 +86,13 @@
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
<!--
<dependency>
<groupId>org.eclipse.jetty.toolchain</groupId>
<artifactId>jetty-test-policy</artifactId>
<version>${jetty.test.policy}</version>
<scope>test</scope>
</dependency>
-->
</dependencies>
<profiles>
<profile>
<id>generate-test-jar</id>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven</groupId>
<artifactId>maven-jar-plugin</artifactId>
</plugin>
</plugins>
</build>
</profile>
</profiles>
</project>

View File

@ -20,6 +20,7 @@ import java.security.Permission;
import java.security.PermissionCollection;
import java.security.Permissions;
import java.security.Policy;
import java.security.Principal;
import java.security.ProtectionDomain;
import java.util.Collections;
import java.util.Enumeration;
@ -78,7 +79,7 @@ public class JettyPolicy extends Policy
{
ProtectionDomain pd = (ProtectionDomain) i.next();
if ( pd.getCodeSource() == null || pd.getCodeSource().implies( domain.getCodeSource() ) )
if ( pd.getCodeSource() == null || pd.getCodeSource().implies( domain.getCodeSource() ) && pd.getPrincipals() == null || validate( pd.getPrincipals(), domain.getPrincipals() ) )
{
// gather dynamic permissions
if ( pdMapping.get( pd ) != null )
@ -136,6 +137,34 @@ public class JettyPolicy extends Policy
return perms;
}
private static boolean validate( Principal[] permCerts, Principal[] classCerts )
{
if ( classCerts == null )
{
return false;
}
for ( int i = 0; i < permCerts.length; ++i )
{
boolean found = false;
for ( int j = 0; j < classCerts.length; ++j )
{
if ( permCerts[i].equals( classCerts[j] ) )
{
found = true;
break;
}
}
// if we didn't find the permCert in the classCerts then we don't match up
if ( found == false )
{
return false;
}
}
return true;
}
public void refresh()
{
try

View File

@ -45,7 +45,7 @@ public class PropertyEvaluator extends HashMap<String,String>
public String getSystemProperty(String name)
{
System.out.println("Prop: " + name + " " + System.getProperty(name));
//System.out.println("Prop: " + name + " " + System.getProperty(name));
if (containsKey(name))
return get(name);

View File

@ -16,24 +16,20 @@ package org.eclipse.jetty.policy.loader;
//and contributed to that project by Alexey V. Varlamov under the ASL
//========================================================================
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.lang.reflect.Constructor;
import java.net.MalformedURLException;
import java.net.URL;
import java.security.AccessController;
import java.security.CodeSource;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.KeyStoreSpi;
import java.security.NoSuchAlgorithmException;
import java.security.Permission;
import java.security.PermissionCollection;
import java.security.Permissions;
import java.security.Principal;
import java.security.ProtectionDomain;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
@ -41,6 +37,7 @@ import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.StringTokenizer;
import org.eclipse.jetty.policy.PolicyException;
@ -70,13 +67,19 @@ public class DefaultPolicyLoader
for ( Iterator<KeystoreEntry> i = keystoreEntries.iterator(); i.hasNext();)
{
keystore = resolveKeyStore( i.next(), evaluator );
if ( keystore != null )
{
// we only process the first valid keystore
break;
}
}
for ( Iterator<GrantEntry> i = grantEntries.iterator(); i.hasNext(); )
{
GrantEntry grant = i.next();
Permissions permissions = processPermissions( grant.permissions, keystore, evaluator );
Permissions permissions = processPermissions( grant, keystore, evaluator );
ProtectionDomain pd;
@ -86,8 +89,11 @@ public class DefaultPolicyLoader
}
else
{
CodeSource codeSource = resolveToCodeSource( grant.codebase, evaluator );
pd = new ProtectionDomain( codeSource, permissions );
Certificate[] certs = resolveToCertificates( keystore, grant.signers );
Principal[] principals = resolvePrincipals( keystore, grant.principals );
CodeSource codeSource = resolveToCodeSource( grant.codebase, certs, evaluator );
pd = new ProtectionDomain( codeSource, permissions, Thread.currentThread().getContextClassLoader(),principals );
//System.out.println( pd.toString() );
}
pdMappings.put( pd, null );
}
@ -100,8 +106,9 @@ public class DefaultPolicyLoader
}
}
private static Permissions processPermissions( Collection<PermissionEntry> collection, KeyStore keystore, PropertyEvaluator evaluator ) throws PolicyException
private static Permissions processPermissions( GrantEntry grant, KeyStore keystore, PropertyEvaluator evaluator ) throws PolicyException
{
Collection<PermissionEntry> collection = grant.permissions;
Permissions permissions = new Permissions();
for ( Iterator<PermissionEntry> i = collection.iterator(); i.hasNext(); )
@ -163,16 +170,57 @@ public class DefaultPolicyLoader
{
throw new PolicyException( e );
}
}
/*
* resolve the use of the klass in the principal entry
*
* @param keystore
* @param collection
* @return
* @throws PolicyException
*/
private static Principal[] resolvePrincipals( KeyStore keystore, Collection<PrincipalEntry> collection ) throws PolicyException
{
if ( keystore == null || collection == null )
{
Principal[] principals = null;
return principals;
}
Set<Principal> principalSet = new HashSet<Principal>();
for ( Iterator<PrincipalEntry> i = collection.iterator(); i.hasNext(); )
{
PrincipalEntry principal = i.next();
try
{
Certificate certificate = keystore.getCertificate( principal.name );
if ( certificate instanceof X509Certificate )
{
principalSet.add( ((X509Certificate) certificate).getSubjectX500Principal() );
}
}
catch ( KeyStoreException kse )
{
throw new PolicyException( kse );
}
}
return principalSet.toArray( new Principal[principalSet.size()]);
}
private static CodeSource resolveToCodeSource( String codeBase, PropertyEvaluator evaluator ) throws PolicyException
private static CodeSource resolveToCodeSource( String codeBase, Certificate[] signers, PropertyEvaluator evaluator ) throws PolicyException
{
try
{
URL url = new URL( evaluator.evaluate(codeBase) );
Certificate[] cert = null;
return new CodeSource( url, cert); //TODO support certificates
return new CodeSource( url, signers);
}
catch ( Exception e )
{
@ -189,16 +237,26 @@ public class DefaultPolicyLoader
* @throws Exception
*/
private static Certificate[] resolveToCertificates( KeyStore keyStore, String signers ) throws PolicyException
{
{
if ( keyStore == null )
{
Certificate[] certs = null;
return certs;
}
Set<Certificate> certificateSet = new HashSet<Certificate>();
StringTokenizer strTok = new StringTokenizer( signers, ",");
Certificate[] certificates = new Certificate[strTok.countTokens()];
for ( int i = 0; strTok.hasMoreTokens(); ++i )
{
try
{
certificates[i] = keyStore.getCertificate( strTok.nextToken().trim() );
{
Certificate certificate = keyStore.getCertificate( strTok.nextToken().trim() );
if ( certificate != null )
{
certificateSet.add( certificate );
}
}
catch ( KeyStoreException kse )
{
@ -206,43 +264,29 @@ public class DefaultPolicyLoader
}
}
return certificates;
return certificateSet.toArray( new Certificate[certificateSet.size()]);
}
private static KeyStore resolveKeyStore( KeystoreEntry entry, PropertyEvaluator evaluator ) throws PolicyException
{
KeyStore keyStore = null;
try
{
KeyStore keyStore = KeyStore.getInstance( entry.type );
URL keyStoreLocation = new URL ( entry.url );
{
keyStore = KeyStore.getInstance( entry.type );
URL keyStoreLocation = new URL ( evaluator.evaluate( entry.url ) );
InputStream istream = keyStoreLocation.openStream();
keyStore.load( istream, null );
return keyStore;
}
catch ( KeyStoreException kse )
catch ( Exception e )
{
throw new PolicyException( kse );
e.printStackTrace();
//throw new PolicyException( kse );
}
catch ( MalformedURLException me )
{
throw new PolicyException( me );
}
catch ( IOException ioe )
{
throw new PolicyException( ioe );
}
catch ( NoSuchAlgorithmException e )
{
throw new PolicyException( e );
}
catch ( CertificateException ce )
{
throw new PolicyException( ce );
}
return keyStore;
}
/**
@ -254,6 +298,11 @@ public class DefaultPolicyLoader
*/
private static boolean validate( Certificate[] permCerts, Certificate[] classCerts )
{
if ( classCerts == null )
{
return false;
}
for ( int i = 0; i < permCerts.length; ++i )
{
boolean found = false;
@ -271,6 +320,7 @@ public class DefaultPolicyLoader
return false;
}
}
// we found all the permCerts in classCerts so return true
return true;
}

View File

@ -25,10 +25,6 @@ import java.util.HashMap;
import java.util.HashSet;
import java.util.Set;
import org.eclipse.jetty.policy.JettyPolicy;
import org.eclipse.jetty.policy.PropertyEvaluator;
import junit.framework.TestCase;
public class TestJettyPolicy extends TestCase
@ -153,6 +149,8 @@ public class TestJettyPolicy extends TestCase
assertFalse( pc.implies( testPerm2 ) );
}
private String getWorkingDirectory()

View File

@ -13,15 +13,15 @@ package org.eclipse.jetty.policy;
//========================================================================
import java.io.File;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
import java.security.AccessControlException;
import java.security.Policy;
import java.util.Collections;
import java.util.HashMap;
import org.eclipse.jetty.policy.JettyPolicy;
import org.eclipse.jetty.policy.PropertyEvaluator;
import junit.framework.TestCase;
public class TestJettyPolicyRuntime extends TestCase
@ -141,7 +141,143 @@ public class TestJettyPolicyRuntime extends TestCase
System.setSecurityManager( null );
Policy.setPolicy( null );
}
public void testCertificateLoader()
throws Exception
{
JettyPolicy ap =
new JettyPolicy( Collections.singleton( getWorkingDirectory()
+ "/src/test/resources/jetty-certificate.policy" ), evaluator );
ap.refresh();
Policy.setPolicy( ap );
System.setSecurityManager( new SecurityManager() );
URL url = new URL("file://" + getWorkingDirectory() + "/target/test-policy/jetty-test-policy-1.0-SNAPSHOT.jar");
URLClassLoader loader ;
if (Thread.currentThread().getContextClassLoader() != null )
{
loader = new URLClassLoader( new URL[]{ url }, Thread.currentThread().getContextClassLoader() );
}
else
{
loader = new URLClassLoader( new URL[]{ url }, ClassLoader.getSystemClassLoader() );
}
Thread.currentThread().setContextClassLoader(loader);
ap.refresh();
try
{
Class clazz = loader.loadClass( "org.eclipse.jetty.toolchain.test.policy.Tester" );
Method m = clazz.getMethod( "testEcho", new Class[] {String.class} );
String foo = (String)m.invoke( clazz.newInstance(), new Object[] {"foo"} );
assertEquals("foo", foo );
Method m2 = clazz.getMethod( "testReadSystemProperty", new Class[] {String.class} );
m2.invoke( clazz.newInstance(), new Object[] {"foo"} );
assertTrue( "system property access was granted", true );
}
catch ( ClassNotFoundException e )
{
e.printStackTrace();
assertFalse( "should not have got here", true );
}
catch ( SecurityException e )
{
e.printStackTrace();
assertFalse( "should not have got here", true );
}
catch ( IllegalAccessException e )
{
e.printStackTrace();
assertFalse( "should not have got here", true );
}
System.setSecurityManager( null );
Policy.setPolicy( null );
}
public void testBadCertificateLoader()
throws Exception
{
JettyPolicy ap =
new JettyPolicy( Collections.singleton( getWorkingDirectory()
+ "/src/test/resources/jetty-bad-certificate.policy" ), evaluator );
ap.refresh();
Policy.setPolicy( ap );
System.setSecurityManager( new SecurityManager() );
URL url = new URL("file://" + getWorkingDirectory() + "/target/test-policy/jetty-test-policy-1.0-SNAPSHOT.jar");
URLClassLoader loader ;
if (Thread.currentThread().getContextClassLoader() != null )
{
loader = new URLClassLoader( new URL[]{ url }, Thread.currentThread().getContextClassLoader() );
}
else
{
loader = new URLClassLoader( new URL[]{ url }, ClassLoader.getSystemClassLoader() );
}
Thread.currentThread().setContextClassLoader(loader);
ap.refresh();
boolean excepted = false;
try
{
Class clazz = loader.loadClass( "org.eclipse.jetty.toolchain.test.policy.Tester" );
Method m = clazz.getMethod( "testEcho", new Class[] {String.class} );
String foo = (String)m.invoke( clazz.newInstance(), new Object[] {"foo"} );
assertEquals("foo", foo );
Method m2 = clazz.getMethod( "testReadSystemProperty", new Class[] {String.class} );
m2.invoke( clazz.newInstance(), new Object[] {"foobar"} );
}
catch ( ClassNotFoundException e )
{
e.printStackTrace();
assertFalse( "should not have got here", true );
}
catch ( InvocationTargetException e )
{
assertTrue(e.getCause().getMessage().contains( "access denied" ));
excepted = true; // we hope to get here
}
catch ( IllegalAccessException e )
{
e.printStackTrace();
assertFalse( "should not have got here", true );
}
assertTrue( "checking that we through a security exception", excepted );
System.setSecurityManager( null );
Policy.setPolicy( null );
}
private String getWorkingDirectory()
{
return System.getProperty( "basedir" ); // TODO work in eclipse

View File

@ -0,0 +1,25 @@
keystore "file://${basedir}/target/test-policy/jetty-policy-nobody.keystore", "jks";
grant signedBy "jetty-policy", codeBase "file://${basedir}/target/test-policy/jetty-test-policy-1.0-SNAPSHOT.jar"
{
permission java.util.PropertyPermission "foo", "read";
}
grant {
permission java.io.FilePermission "/-", "read, write";
permission java.lang.RuntimePermission "setContextClassLoader";
permission java.lang.RuntimePermission "setSecurityManager";
permission java.security.SecurityPermission "getPolicy";
permission java.lang.RuntimePermission "createClassLoader";
permission java.lang.RuntimePermission "setIO";
permission java.util.PropertyPermission "java.class.path", "read,write";
permission java.util.PropertyPermission "basedir", "read";
}

View File

@ -0,0 +1,22 @@
keystore "file://${basedir}/target/test-policy/jetty-policy.keystore", "jks";
grant signedBy "jetty-policy-bad", codeBase "file://${basedir}/target/test-policy/jetty-test-policy-1.0-SNAPSHOT.jar"
{
permission java.util.PropertyPermission "foo", "read";
};
grant {
permission java.io.FilePermission "/-", "read, write";
permission java.lang.RuntimePermission "setContextClassLoader";
permission java.lang.RuntimePermission "setSecurityManager";
permission java.security.SecurityPermission "getPolicy";
permission java.lang.RuntimePermission "createClassLoader";
permission java.lang.RuntimePermission "setIO";
permission java.util.PropertyPermission "java.class.path", "read,write";
permission java.util.PropertyPermission "basedir", "read";
};