From 62ff473a501db282c893899987441f66f2313145 Mon Sep 17 00:00:00 2001 From: Jesse McConnell Date: Thu, 25 Jun 2009 21:00:56 +0000 Subject: [PATCH] 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 --- jetty-policy/pom.xml | 71 +++++++-- .../org/eclipse/jetty/policy/JettyPolicy.java | 31 +++- .../jetty/policy/PropertyEvaluator.java | 2 +- .../policy/loader/DefaultPolicyLoader.java | 138 +++++++++++------ .../eclipse/jetty/policy/TestJettyPolicy.java | 6 +- .../jetty/policy/TestJettyPolicyRuntime.java | 144 +++++++++++++++++- .../resources/jetty-bad-certificate.policy | 25 +++ .../test/resources/jetty-certificate.policy | 22 +++ 8 files changed, 372 insertions(+), 67 deletions(-) create mode 100644 jetty-policy/src/test/resources/jetty-bad-certificate.policy create mode 100644 jetty-policy/src/test/resources/jetty-certificate.policy diff --git a/jetty-policy/pom.xml b/jetty-policy/pom.xml index a82b7ff20a4..582673f9437 100644 --- a/jetty-policy/pom.xml +++ b/jetty-policy/pom.xml @@ -9,6 +9,10 @@ jetty-policy Jetty :: Policy Tool jar + + 1.0-SNAPSHOT + target/test-policy + @@ -28,6 +32,52 @@ + + org.apache.maven.plugins + maven-dependency-plugin + + + unpack + generate-resources + + unpack + + + + + org.eclipse.jetty.toolchain + jetty-test-policy + ${jetty.test.policy} + jar + true + **/*.keystore + ${jetty.test.policy.loc} + + + + + + copy + generate-resources + + copy + + + + + org.eclipse.jetty.toolchain + jetty-test-policy + ${jetty.test.policy} + jar + true + ** + ${jetty.test.policy.loc} + + + + + + @@ -36,18 +86,13 @@ junit test + - - - generate-test-jar - - - - org.apache.maven - maven-jar-plugin - - - - - diff --git a/jetty-policy/src/main/java/org/eclipse/jetty/policy/JettyPolicy.java b/jetty-policy/src/main/java/org/eclipse/jetty/policy/JettyPolicy.java index 6aed5149f14..b01eef773e1 100644 --- a/jetty-policy/src/main/java/org/eclipse/jetty/policy/JettyPolicy.java +++ b/jetty-policy/src/main/java/org/eclipse/jetty/policy/JettyPolicy.java @@ -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 diff --git a/jetty-policy/src/main/java/org/eclipse/jetty/policy/PropertyEvaluator.java b/jetty-policy/src/main/java/org/eclipse/jetty/policy/PropertyEvaluator.java index 508cc4b4da6..02e1e660371 100644 --- a/jetty-policy/src/main/java/org/eclipse/jetty/policy/PropertyEvaluator.java +++ b/jetty-policy/src/main/java/org/eclipse/jetty/policy/PropertyEvaluator.java @@ -45,7 +45,7 @@ public class PropertyEvaluator extends HashMap 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); diff --git a/jetty-policy/src/main/java/org/eclipse/jetty/policy/loader/DefaultPolicyLoader.java b/jetty-policy/src/main/java/org/eclipse/jetty/policy/loader/DefaultPolicyLoader.java index ef19cea01dd..e9c26de8906 100644 --- a/jetty-policy/src/main/java/org/eclipse/jetty/policy/loader/DefaultPolicyLoader.java +++ b/jetty-policy/src/main/java/org/eclipse/jetty/policy/loader/DefaultPolicyLoader.java @@ -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 i = keystoreEntries.iterator(); i.hasNext();) { keystore = resolveKeyStore( i.next(), evaluator ); + + if ( keystore != null ) + { + // we only process the first valid keystore + break; + } } for ( Iterator 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 collection, KeyStore keystore, PropertyEvaluator evaluator ) throws PolicyException + private static Permissions processPermissions( GrantEntry grant, KeyStore keystore, PropertyEvaluator evaluator ) throws PolicyException { + Collection collection = grant.permissions; Permissions permissions = new Permissions(); for ( Iterator 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 collection ) throws PolicyException + { + if ( keystore == null || collection == null ) + { + Principal[] principals = null; + return principals; + } + + Set principalSet = new HashSet(); + + + for ( Iterator 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 certificateSet = new HashSet(); 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; } diff --git a/jetty-policy/src/test/java/org/eclipse/jetty/policy/TestJettyPolicy.java b/jetty-policy/src/test/java/org/eclipse/jetty/policy/TestJettyPolicy.java index 94b8a5dabaa..1af41cc1949 100644 --- a/jetty-policy/src/test/java/org/eclipse/jetty/policy/TestJettyPolicy.java +++ b/jetty-policy/src/test/java/org/eclipse/jetty/policy/TestJettyPolicy.java @@ -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() diff --git a/jetty-policy/src/test/java/org/eclipse/jetty/policy/TestJettyPolicyRuntime.java b/jetty-policy/src/test/java/org/eclipse/jetty/policy/TestJettyPolicyRuntime.java index e2d356dedbb..f1151c45cef 100644 --- a/jetty-policy/src/test/java/org/eclipse/jetty/policy/TestJettyPolicyRuntime.java +++ b/jetty-policy/src/test/java/org/eclipse/jetty/policy/TestJettyPolicyRuntime.java @@ -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 diff --git a/jetty-policy/src/test/resources/jetty-bad-certificate.policy b/jetty-policy/src/test/resources/jetty-bad-certificate.policy new file mode 100644 index 00000000000..1220210c859 --- /dev/null +++ b/jetty-policy/src/test/resources/jetty-bad-certificate.policy @@ -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"; + + +} \ No newline at end of file diff --git a/jetty-policy/src/test/resources/jetty-certificate.policy b/jetty-policy/src/test/resources/jetty-certificate.policy new file mode 100644 index 00000000000..3d6092844ba --- /dev/null +++ b/jetty-policy/src/test/resources/jetty-certificate.policy @@ -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"; +}; \ No newline at end of file