diff options
author | Sven Gothel <[email protected]> | 2011-09-23 13:17:36 +0200 |
---|---|---|
committer | Sven Gothel <[email protected]> | 2011-09-23 13:17:36 +0200 |
commit | 9da5bc1fe999caa924bd8dceafeff93ddf9d16a0 (patch) | |
tree | 54a0206d47b03d1d914bb55707b52b2cabd829b5 | |
parent | 675ea2c9ee6308d439b2429d0863a634ba673eff (diff) |
TempJarCache/JNILibLoaderBase: Validate the to be loader JarFile's Certificates if caller has any. Add Convenient JNILibLoaderBase.addNativeJarLibs(..) method.
-rw-r--r-- | src/java/com/jogamp/common/jvm/JNILibLoaderBase.java | 30 | ||||
-rw-r--r-- | src/java/com/jogamp/common/os/Platform.java | 25 | ||||
-rw-r--r-- | src/java/com/jogamp/common/util/JarUtil.java | 80 | ||||
-rw-r--r-- | src/java/com/jogamp/common/util/cache/TempJarCache.java | 61 | ||||
-rw-r--r-- | src/junit/com/jogamp/common/util/TestTempJarCache.java | 21 |
5 files changed, 158 insertions, 59 deletions
diff --git a/src/java/com/jogamp/common/jvm/JNILibLoaderBase.java b/src/java/com/jogamp/common/jvm/JNILibLoaderBase.java index 44cbd14..bb0860a 100644 --- a/src/java/com/jogamp/common/jvm/JNILibLoaderBase.java +++ b/src/java/com/jogamp/common/jvm/JNILibLoaderBase.java @@ -39,12 +39,17 @@ package com.jogamp.common.jvm; +import java.io.IOException; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; +import java.net.URL; import java.security.AccessController; import java.security.AccessControlContext; import java.util.HashSet; +import java.util.jar.JarFile; +import com.jogamp.common.os.Platform; +import com.jogamp.common.util.JarUtil; import com.jogamp.common.util.cache.TempJarCache; import jogamp.common.Debug; @@ -135,6 +140,31 @@ public class JNILibLoaderBase { loaderAction = action; } + public static final boolean addNativeJarLibs(Class<?> classFromJavaJar, String nativeJarBaseName) { + if(TempJarCache.isInitialized()) { + final String nativeJarName = nativeJarBaseName+"-natives-"+Platform.getOSAndArch()+".jar"; + final ClassLoader cl = classFromJavaJar.getClassLoader(); + try { + URL jarUrlRoot = JarUtil.getJarURLDirname( JarUtil.getJarURL( classFromJavaJar.getName(), cl ) ); + if(DEBUG) { + System.err.println("addNativeJarLibs: "+nativeJarBaseName+": url-root "+jarUrlRoot); + } + URL nativeJarURL = JarUtil.getJarURL(jarUrlRoot, nativeJarName); + if(DEBUG) { + System.err.println("addNativeJarLibs: "+nativeJarBaseName+": nativeJarURL "+nativeJarURL); + } + JarFile nativeJar = JarUtil.getJarFile(nativeJarURL, cl); + if(DEBUG) { + System.err.println("addNativeJarLibs: "+nativeJarBaseName+": nativeJar "+nativeJar.getName()); + } + return TempJarCache.addNativeLibs(classFromJavaJar, nativeJar); + } catch (IOException ioe) { + ioe.printStackTrace(); + } + } + return false; + } + protected static synchronized boolean loadLibrary(String libname, boolean ignoreError) { if (loaderAction != null) { return loaderAction.loadLibrary(libname, ignoreError); diff --git a/src/java/com/jogamp/common/os/Platform.java b/src/java/com/jogamp/common/os/Platform.java index 3627513..38635e1 100644 --- a/src/java/com/jogamp/common/os/Platform.java +++ b/src/java/com/jogamp/common/os/Platform.java @@ -285,22 +285,19 @@ public class Platform { public Object run() { if(TempJarCache.initSingleton()) { try { - URL jarUrlRoot = JarUtil.getJarURLDirname( + final URL jarUrlRoot = JarUtil.getJarURLDirname( JarUtil.getJarURL(Platform.class.getName(), cl) ); - System.err.println("gluegen-rt: url-root "+jarUrlRoot); - URL nativeJarURL = JarUtil.getJarURL(jarUrlRoot, nativeJarName); - System.err.println("gluegen-rt: nativeJarURL "+nativeJarURL); - JarFile nativeJar = JarUtil.getJarFile(nativeJarURL, cl); - System.err.println("gluegen-rt: nativeJar "+nativeJar.getName()); - TempJarCache.bootstrapNativeLib(libBaseName, nativeJar); - } catch (IOException ioe) { - ioe.printStackTrace(); - } - } - DynamicLibraryBundle.GlueJNILibLoader.loadLibrary(libBaseName, false); - return null; + final URL nativeJarURL = JarUtil.getJarURL(jarUrlRoot, nativeJarName); + final JarFile nativeJar = JarUtil.getJarFile(nativeJarURL, cl); + TempJarCache.bootstrapNativeLib(Platform.class, libBaseName, nativeJar); + } catch (IOException ioe) { + ioe.printStackTrace(); } - }); + } + DynamicLibraryBundle.GlueJNILibLoader.loadLibrary(libBaseName, false); + return null; + } + }); } /** diff --git a/src/java/com/jogamp/common/util/JarUtil.java b/src/java/com/jogamp/common/util/JarUtil.java index 3cbd555..4f46ca8 100644 --- a/src/java/com/jogamp/common/util/JarUtil.java +++ b/src/java/com/jogamp/common/util/JarUtil.java @@ -55,6 +55,27 @@ public class JarUtil { /** * @param clazzBinName com.jogamp.common.util.cache.TempJarCache * @param cl + * @return gluegen-rt.jar + * @throws IOException + * @see {@link IOUtil#getClassURL(String, ClassLoader)} + */ + public static String getJarName(String clazzBinName, ClassLoader cl) throws IOException { + URL url = IOUtil.getClassURL(clazzBinName, cl); + if(null != url) { + String urlS = url.toExternalForm(); + // from + // jar:file:/usr/local/projects/JOGL/gluegen/build-x86_64/gluegen-rt.jar!/com/jogamp/common/util/cache/TempJarCache.class + // to + // gluegen-rt.jar + urlS = urlS.substring(0, urlS.lastIndexOf('!')); // exclude !/ + return urlS.substring(urlS.lastIndexOf('/')+1); // just the jar name + } + return null; + } + + /** + * @param clazzBinName com.jogamp.common.util.cache.TempJarCache + * @param cl * @return jar:file:/usr/local/projects/JOGL/gluegen/build-x86_64/gluegen-rt.jar!/ * @throws IOException * @see {@link IOUtil#getClassURL(String, ClassLoader)} @@ -195,11 +216,11 @@ public class JarUtil { * @return * @throws IOException */ - public static int extract(File dest, Map<String, String> nativeLibMap, - JarFile jarFile, - boolean extractNativeLibraries, - boolean extractClassFiles, - boolean extractOtherFiles) throws IOException { + public static final int extract(File dest, Map<String, String> nativeLibMap, + JarFile jarFile, + boolean extractNativeLibraries, + boolean extractClassFiles, + boolean extractOtherFiles) throws IOException { if (VERBOSE) { System.err.println("extract: "+jarFile.getName()+" -> "+dest+ @@ -280,30 +301,25 @@ public class JarUtil { * Validate the certificates for each native Lib in the jar file. * Throws an IOException if any certificate is not valid. * <pre> - Certificate[] appletLauncherCerts = Something.class.getProtectionDomain(). - getCodeSource().getCertificates(); + Certificate[] rootCerts = Something.class.getProtectionDomain(). + getCodeSource().getCertificates(); </pre> */ - public static void validateCertificates(Certificate[] appletLauncherCerts, JarFile jarFile) - throws IOException { + public static final void validateCertificates(Certificate[] rootCerts, JarFile jarFile) + throws IOException, SecurityException { if (VERBOSE) { - System.err.println("validateCertificates:"); + System.err.println("validateCertificates: "+jarFile.getName()); + } + + if (rootCerts == null || rootCerts.length == 0) { + throw new IllegalArgumentException("Null certificates passed"); } - byte[] buf = new byte[1000]; + byte[] buf = new byte[1024]; Enumeration<JarEntry> entries = jarFile.entries(); while (entries.hasMoreElements()) { - JarEntry entry = (JarEntry) entries.nextElement(); - String entryName = entry.getName(); - - if (VERBOSE) { - System.err.println("Validate JarEntry : " + entryName); - } - - if (!checkNativeCertificates(appletLauncherCerts, jarFile, entry, buf)) { - throw new IOException("Cannot validate certificate for " + entryName); - } + validateCertificate(rootCerts, jarFile, entries.nextElement(), buf); } } @@ -311,8 +327,12 @@ public class JarUtil { * Check the certificates with the ones in the jar file * (all must match). */ - private static boolean checkNativeCertificates(Certificate[] launchedCerts, - JarFile jar, JarEntry entry, byte[] buf) throws IOException { + private static final void validateCertificate(Certificate[] rootCerts, + JarFile jar, JarEntry entry, byte[] buf) throws IOException, SecurityException { + + if (VERBOSE) { + System.err.println("Validate JarEntry : " + entry.getName()); + } // API states that we must read all of the data from the entry's // InputStream in order to be able to get its certificates @@ -321,25 +341,23 @@ public class JarUtil { while (is.read(buf) > 0) { } is.close(); - if (launchedCerts == null || launchedCerts.length == 0) { - throw new RuntimeException("Null certificates passed"); - } - // Get the certificates for the JAR entry Certificate[] nativeCerts = entry.getCertificates(); if (nativeCerts == null || nativeCerts.length == 0) { - return false; + throw new SecurityException("no certificate for " + entry.getName() + " in " + jar.getName()); } int checked = 0; - for (int i = 0; i < launchedCerts.length; i++) { + for (int i = 0; i < rootCerts.length; i++) { for (int j = 0; j < nativeCerts.length; j++) { - if (nativeCerts[j].equals(launchedCerts[i])){ + if (nativeCerts[j].equals(rootCerts[i])){ checked++; break; } } } - return (checked == launchedCerts.length); + if( checked != rootCerts.length ) { + throw new SecurityException("not all certificates match, only "+checked+" out of "+rootCerts.length+" for " + entry.getName() + " in " + jar.getName()); + } } } diff --git a/src/java/com/jogamp/common/util/cache/TempJarCache.java b/src/java/com/jogamp/common/util/cache/TempJarCache.java index dcc6651..7e90f7d 100644 --- a/src/java/com/jogamp/common/util/cache/TempJarCache.java +++ b/src/java/com/jogamp/common/util/cache/TempJarCache.java @@ -34,6 +34,7 @@ import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; +import java.security.cert.Certificate; import java.util.Enumeration; import java.util.HashMap; import java.util.HashSet; @@ -120,13 +121,17 @@ public class TempJarCache { /** * Adds native libraries, if not yet added. * + * @param certClass if class is certified, the JarFile entries needs to have the same certificate * @param jarFile + * * @return * @throws IOException + * @throws SecurityException */ - public static boolean addNativeLibs(JarFile jarFile) throws IOException { + public static final boolean addNativeLibs(Class<?> certClass, JarFile jarFile) throws IOException, SecurityException { checkInitialized(); if(!nativeLibJars.contains(jarFile)) { + validateCertificates(certClass, jarFile); JarUtil.extract(tmpFileCache.getTempDir(), nativeLibMap, jarFile, true, false, false); nativeLibJars.add(jarFile); @@ -140,14 +145,18 @@ public class TempJarCache { * * TODO class access pending * needs Classloader.defineClass(..) access, ie. own derivation - will do when needed .. - * + * + * @param certClass if class is certified, the JarFile entries needs to have the same certificate * @param jarFile + * * @return * @throws IOException + * @throws SecurityException */ - public static boolean addClasses(JarFile jarFile) throws IOException { + public static final boolean addClasses(Class<?> certClass, JarFile jarFile) throws IOException, SecurityException { checkInitialized(); if(!classFileJars.contains(jarFile)) { + validateCertificates(certClass, jarFile); JarUtil.extract(tmpFileCache.getTempDir(), null, jarFile, false, true, false); classFileJars.add(jarFile); @@ -159,13 +168,17 @@ public class TempJarCache { /** * Adds native resources, if not yet added. * + * @param certClass if class is certified, the JarFile entries needs to have the same certificate * @param jarFile + * * @return * @throws IOException + * @throws SecurityException */ - public static boolean addResources(JarFile jarFile) throws IOException { + public static final boolean addResources(Class<?> certClass, JarFile jarFile) throws IOException, SecurityException { checkInitialized(); if(!resourceFileJars.contains(jarFile)) { + validateCertificates(certClass, jarFile); JarUtil.extract(tmpFileCache.getTempDir(), null, jarFile, false, false, true); resourceFileJars.add(jarFile); @@ -180,12 +193,15 @@ public class TempJarCache { * * TODO class access pending * needs Classloader.defineClass(..) access, ie. own derivation - will do when needed .. - * + * + * @param certClass if class is certified, the JarFile entries needs to have the same certificate * @param jarFile + * * @return * @throws IOException + * @throws SecurityException */ - public static boolean addAll(JarFile jarFile) throws IOException { + public static final boolean addAll(Class<?> certClass, JarFile jarFile) throws IOException, SecurityException { checkInitialized(); if(!nativeLibJars.contains(jarFile) || !classFileJars.contains(jarFile) || @@ -193,6 +209,7 @@ public class TempJarCache { final boolean extractNativeLibraries = !nativeLibJars.contains(jarFile); final boolean extractClassFiles = !classFileJars.contains(jarFile); final boolean extractOtherFiles = !resourceFileJars.contains(jarFile); + validateCertificates(certClass, jarFile); JarUtil.extract(tmpFileCache.getTempDir(), nativeLibMap, jarFile, extractNativeLibraries, extractClassFiles, extractOtherFiles); if(extractNativeLibraries) { @@ -209,7 +226,7 @@ public class TempJarCache { return false; } - public static String findLibrary(String libName) { + public static final String findLibrary(String libName) { checkInitialized(); // try with mapped library basename first String path = nativeLibMap.get(libName); @@ -240,7 +257,7 @@ public class TempJarCache { return null; } */ - public static String findResource(String name) { + public static final String findResource(String name) { checkInitialized(); final File f = new File(tmpFileCache.getTempDir(), name); if(f.exists()) { @@ -252,12 +269,19 @@ public class TempJarCache { /** * Bootstrapping version extracting the JAR files root entry containing libBaseName, * assuming it's a native library. This is used to get the 'gluegen-rt' - * native library, hence bootstrapping. + * native library, hence bootstrapping. + * + * @param certClass if class is certified, the JarFile entries needs to have the same certificate + * + * @throws IOException + * @throws SecurityException */ - public static boolean bootstrapNativeLib(String libBaseName, JarFile jarFile) throws IOException { + public static final boolean bootstrapNativeLib(Class<?> certClass, String libBaseName, JarFile jarFile) + throws IOException, SecurityException { checkInitialized(); if(!nativeLibJars.contains(jarFile) && !nativeLibMap.containsKey(libBaseName) ) { - final Enumeration<JarEntry> entries = jarFile.entries(); + validateCertificates(certClass, jarFile); + final Enumeration<JarEntry> entries = jarFile.entries(); while (entries.hasMoreElements()) { final JarEntry entry = (JarEntry) entries.nextElement(); final String entryName = entry.getName(); @@ -289,4 +313,19 @@ public class TempJarCache { } return false; } + + private static void validateCertificates(Class<?> certClass, JarFile jarFile) throws IOException, SecurityException { + if(null == certClass) { + throw new IllegalArgumentException("certClass is null"); + } + final Certificate[] rootCerts = + certClass.getProtectionDomain().getCodeSource().getCertificates(); + if( null != rootCerts && rootCerts.length>0 ) { + // Only validate the jarFile's certs with ours, if we have any. + // Otherwise we may run uncertified JARs (application). + // In case one tries to run uncertified JARs, the wrapping applet/JNLP + // SecurityManager will kick in and throw a SecurityException. + JarUtil.validateCertificates(rootCerts, jarFile); + } + } } diff --git a/src/junit/com/jogamp/common/util/TestTempJarCache.java b/src/junit/com/jogamp/common/util/TestTempJarCache.java index fca839b..e1e47af 100644 --- a/src/junit/com/jogamp/common/util/TestTempJarCache.java +++ b/src/junit/com/jogamp/common/util/TestTempJarCache.java @@ -41,6 +41,7 @@ import org.junit.BeforeClass; import org.junit.Test; import com.jogamp.common.GlueGenVersion; +import com.jogamp.common.jvm.JNILibLoaderBase; import com.jogamp.common.os.NativeLibrary; import com.jogamp.common.os.Platform; import com.jogamp.common.util.cache.TempCacheReg; @@ -161,7 +162,7 @@ public class TestTempJarCache { Assert.assertTrue(TempCacheReg.isTempJarCacheUsed()); Assert.assertTrue(TempJarCache.isInitialized()); - TempJarCache.addAll(JarUtil.getJarFile(GlueGenVersion.class.getName(), getClass().getClassLoader())); + TempJarCache.addAll(GlueGenVersion.class, JarUtil.getJarFile(GlueGenVersion.class.getName(), getClass().getClassLoader())); File f0 = new File(TempJarCache.getTempFileCache().getTempDir(), "META-INF/MANIFEST.MF"); Assert.assertTrue(f0.exists()); @@ -179,7 +180,7 @@ public class TestTempJarCache { } @Test - public void testTempJarCache02LoadNativeLibrary() throws IOException { + public void testTempJarCache02AddNativeLibs() throws IOException { final String nativeJarName = "gluegen-rt-natives-"+Platform.getOSAndArch()+".jar"; final String libBaseName = "gluegen-rt"; final ClassLoader cl = getClass().getClassLoader(); @@ -190,7 +191,21 @@ public class TestTempJarCache { URL nativeJarURL = JarUtil.getJarURL(jarUrlRoot, nativeJarName); JarFile nativeJar = JarUtil.getJarFile(nativeJarURL, cl); - TempJarCache.addNativeLibs(nativeJar); + TempJarCache.addNativeLibs(TempJarCache.class, nativeJar); + String libFullPath = TempJarCache.findLibrary(libBaseName); + Assert.assertNotNull(libFullPath); + Assert.assertEquals(libBaseName, NativeLibrary.isValidNativeLibraryName(libFullPath, true)); + File f = new File(libFullPath); + Assert.assertTrue(f.exists()); + } + + @Test + public void testTempJarCache03AddNativeJarLibs() throws IOException { + final String libBaseName = "gluegen-rt"; + + JNILibLoaderBase.addNativeJarLibs(TempJarCache.class, libBaseName); + Assert.assertTrue(JNILibLoaderBase.isLoaded(libBaseName)); + String libFullPath = TempJarCache.findLibrary(libBaseName); Assert.assertNotNull(libFullPath); Assert.assertEquals(libBaseName, NativeLibrary.isValidNativeLibraryName(libFullPath, true)); |