diff --git a/WHATSNEW b/WHATSNEW index dbc7397c7..3f4a8d7f0 100644 --- a/WHATSNEW +++ b/WHATSNEW @@ -410,7 +410,10 @@ Fixed bugs: * The default stylesheets for failed to properly escape XML content in exception stack traces. - Bugzilla Report 39492 + Bugzilla Report 39492. + + * AntClassLoader didn't set the proper CodeSource for loaded classes. + Bugzilla Report 20174. Other changes: -------------- diff --git a/src/etc/testcases/core/antclassloader.xml b/src/etc/testcases/core/antclassloader.xml index a8b817354..96f040587 100644 --- a/src/etc/testcases/core/antclassloader.xml +++ b/src/etc/testcases/core/antclassloader.xml @@ -67,4 +67,9 @@ public class Foo {} + + + + diff --git a/src/main/org/apache/tools/ant/AntClassLoader.java b/src/main/org/apache/tools/ant/AntClassLoader.java index 2c063aeb7..8743db3f6 100644 --- a/src/main/org/apache/tools/ant/AntClassLoader.java +++ b/src/main/org/apache/tools/ant/AntClassLoader.java @@ -27,6 +27,9 @@ import java.io.Reader; import java.lang.reflect.Constructor; import java.net.MalformedURLException; import java.net.URL; +import java.security.CodeSource; +import java.security.ProtectionDomain; +import java.security.cert.Certificate; import java.util.Collections; import java.util.Enumeration; import java.util.HashMap; @@ -37,6 +40,7 @@ import java.util.Vector; import java.util.Locale; import java.util.jar.Attributes; import java.util.jar.Attributes.Name; +import java.util.jar.JarEntry; import java.util.jar.JarFile; import java.util.jar.Manifest; import java.util.zip.ZipEntry; @@ -1116,11 +1120,17 @@ public class AntClassLoader extends ClassLoader implements SubBuildListener { protected Class defineClassFromData(File container, byte[] classData, String classname) throws IOException { definePackage(container, classname); - // XXX should instead make a new ProtectionDomain with a CodeSource - // corresponding to container.toURI().toURL() and the same - // PermissionCollection as Project.class.protectionDomain had - return defineClass(classname, classData, 0, classData.length, Project.class - .getProtectionDomain()); + ProtectionDomain currentPd = Project.class.getProtectionDomain(); + String classResource = getClassFilename(classname); + CodeSource src = new CodeSource(FILE_UTILS.getFileURL(container), + getCertificates(container, + classResource)); + ProtectionDomain classesPd = + new ProtectionDomain(src, currentPd.getPermissions(), + this, + currentPd.getPrincipals()); + return defineClass(classname, classData, 0, classData.length, + classesPd); } /** @@ -1179,6 +1189,41 @@ public class AntClassLoader extends ClassLoader implements SubBuildListener { } } + /** + * Get the certificates for a given jar entry, if it is indeed a jar. + * + * @param container the File from which to read the entry + * @param entry the entry of which the certificates are requested + * + * @return the entry's certificates or null is the container is + * not a jar or it has no certificates. + * + * @exception IOException if the manifest cannot be read. + */ + private Certificate[] getCertificates(File container, String entry) + throws IOException { + if (container.isDirectory()) { + return null; + } + JarFile jarFile = null; + InputStream is = null; + try { + jarFile = new JarFile(container); + JarEntry ent = jarFile.getJarEntry(entry); + if (ent != null) { + // must read the input in order to obtain certificates + is = jarFile.getInputStream(ent); + while (is.read() >= 0); + } + return ent == null ? null : ent.getCertificates(); + } finally { + FileUtils.close(is); + if (jarFile != null) { + jarFile.close(); + } + } + } + /** * Define the package information when the class comes from a * jar with a manifest diff --git a/src/tests/junit/org/apache/tools/ant/AntClassLoaderTest.java b/src/tests/junit/org/apache/tools/ant/AntClassLoaderTest.java index 5aa658952..7a414d069 100644 --- a/src/tests/junit/org/apache/tools/ant/AntClassLoaderTest.java +++ b/src/tests/junit/org/apache/tools/ant/AntClassLoaderTest.java @@ -28,6 +28,7 @@ import org.apache.tools.ant.types.Path; public class AntClassLoaderTest extends BuildFileTest { private Project p; + private AntClassLoader loader; public AntClassLoaderTest(String name) { super(name); @@ -41,6 +42,9 @@ public class AntClassLoaderTest extends BuildFileTest { } public void tearDown() { + if (loader != null) { + loader.cleanup(); + } getProject().executeTarget("cleanup"); } //test inspired by bug report 37085 @@ -50,8 +54,8 @@ public class AntClassLoaderTest extends BuildFileTest { Path myPath = new Path(getProject()); myPath.setLocation(new File(mainjarstring)); getProject().setUserProperty("build.sysclasspath","ignore"); - AntClassLoader myLoader = getProject().createClassLoader(myPath); - String path = myLoader.getClasspath(); + loader = getProject().createClassLoader(myPath); + String path = loader.getClasspath(); assertEquals(mainjarstring + File.pathSeparator + extjarstring, path); } public void testJarWithManifestInNonAsciiDir() { @@ -60,13 +64,13 @@ public class AntClassLoaderTest extends BuildFileTest { Path myPath = new Path(getProject()); myPath.setLocation(new File(mainjarstring)); getProject().setUserProperty("build.sysclasspath","ignore"); - AntClassLoader myLoader = getProject().createClassLoader(myPath); - String path = myLoader.getClasspath(); + loader = getProject().createClassLoader(myPath); + String path = loader.getClasspath(); assertEquals(mainjarstring + File.pathSeparator + extjarstring, path); } public void testCleanup() throws BuildException { Path path = new Path(p, "."); - AntClassLoader loader = p.createClassLoader(path); + loader = p.createClassLoader(path); try { // we don't expect to find this loader.findClass("fubar"); @@ -105,12 +109,44 @@ public class AntClassLoaderTest extends BuildFileTest { myPath.setLocation(new File(getProject().getProperty("tmp.dir") + "/test.jar")); getProject().setUserProperty("build.sysclasspath","ignore"); - AntClassLoader loader = getProject().createClassLoader(myPath); + loader = getProject().createClassLoader(myPath); assertNotNull("should find class", loader.findClass("org.example.Foo")); assertNotNull("should find package", new GetPackageWrapper(loader).getPackage("org.example")); } + public void testCodeSource() throws Exception { + executeTarget("prepareGetPackageTest"); + Path myPath = new Path(getProject()); + myPath.setLocation(new File(getProject().getProperty("tmp.dir") + + "/test.jar")); + getProject().setUserProperty("build.sysclasspath","ignore"); + loader = getProject().createClassLoader(myPath); + Class foo = loader.findClass("org.example.Foo"); + String codeSourceLocation = + foo.getProtectionDomain().getCodeSource().getLocation().toString(); + assertTrue(codeSourceLocation + " should point to test.jar", + codeSourceLocation.endsWith("test.jar")); + } + + public void testSignedJar() throws Exception { + executeTarget("signTestJar"); + File jar = new File(getProject().getProperty("tmp.dir") + + "/test.jar"); + + Path myPath = new Path(getProject()); + myPath.setLocation(jar); + getProject().setUserProperty("build.sysclasspath","ignore"); + loader = getProject().createClassLoader(myPath); + Class foo = loader.findClass("org.example.Foo"); + + assertNotNull("should find class", foo); + assertNotNull("should have certificates", + foo.getProtectionDomain().getCodeSource() + .getCertificates()); + assertNotNull("should be signed", foo.getSigners()); + } + private static class GetPackageWrapper extends ClassLoader { GetPackageWrapper(ClassLoader parent) { super(parent);