From 5c82452d5e291a005f2e4dd957b62562daf928ac Mon Sep 17 00:00:00 2001 From: Brian Matzon Date: Mon, 3 Jul 2006 23:22:46 +0000 Subject: [PATCH] added certificate check reworked installer to expect a jar with the platform natives inside a jar now installs into java.io.tmpdir/.lwjglinstall/ uninstaller checks for removed watermark file - tho it never gets removed *grumble* --- .../org/lwjgl/util/applet/LWJGLInstaller.java | 174 ++++++++++++------ 1 file changed, 116 insertions(+), 58 deletions(-) diff --git a/src/java/org/lwjgl/util/applet/LWJGLInstaller.java b/src/java/org/lwjgl/util/applet/LWJGLInstaller.java index 59b55f1e..f7d7397a 100644 --- a/src/java/org/lwjgl/util/applet/LWJGLInstaller.java +++ b/src/java/org/lwjgl/util/applet/LWJGLInstaller.java @@ -33,6 +33,8 @@ package org.lwjgl.util.applet; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileFilter; import java.io.FileOutputStream; @@ -41,9 +43,18 @@ import java.io.InputStream; import java.io.OutputStream; import java.security.AccessController; import java.security.PrivilegedAction; +import java.security.PrivilegedActionException; import java.security.PrivilegedExceptionAction; +import java.security.cert.Certificate; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.jar.JarEntry; +import java.util.jar.JarFile; +import java.util.jar.JarInputStream; import org.lwjgl.LWJGLUtil; +import org.lwjgl.Sys; /** *

@@ -55,16 +66,6 @@ import org.lwjgl.LWJGLUtil; */ public class LWJGLInstaller { - /** - * Files to install for each supported platform - * @see org.lwjgl.LWJGLUtil#getPlatform() - */ - public static final String[][] PLATFORM_FILES = { - { "lwjgl", "lwjgl-fmod3", "lwjgl-devil", "openal", "fmod", "IL", "ILU", "ILUT", "jinput-osx"}, - { "lwjgl", "lwjgl-fmod3", "lwjgl-devil", "openal", "fmod", "IL", "ILU", "ILUT", "jinput-linux"}, - { "lwjgl", "lwjgl-fmod3", "lwjgl-devil", "OpenAL32", "fmod", "DevIL", "ILU", "ILUT", "jinput-dx8", "jinput-raw"} - }; - /** Whether the installer has been called */ public static boolean installed; @@ -73,6 +74,12 @@ public class LWJGLInstaller { /** Buffer used when copying files */ private static final byte[] COPY_BUFFER = new byte[4096]; + + /** Directory all lwjgl installations go into */ + public static String MASTER_INSTALL_DIR = ".lwjglinstall"; + + /** Name of the native jar we're expected to load and install */ + public static final String NATIVES_PLATFORM_JAR = "/" + LWJGLUtil.getPlatformName() + "_natives.jar"; private LWJGLInstaller() { /* Unused */ @@ -99,11 +106,13 @@ public class LWJGLInstaller { } try { - // libraries to validate and install - String[] libraries = PLATFORM_FILES[LWJGLUtil.getPlatform() - 1]; - - // Validate the certificates of the native files - validateCertificates(); + // Validate the certificates of the platform native jar + HashMap files = (HashMap) + AccessController.doPrivileged(new PrivilegedExceptionAction() { + public Object run() throws PrivilegedActionException{ + return validateCertificates(); + } + }); // install shutdown installer hook if(!disableUninstall) { @@ -123,11 +132,8 @@ public class LWJGLInstaller { String user_temp_dir = getPriviledgedString("java.io.tmpdir"); final String path = createTemporaryDir(user_temp_dir); - // extract natives - for (int i = 0; i < libraries.length; i++) { - String library = System.mapLibraryName(libraries[i]); - extract(library, path); - } + // extract natives from jar + writeFiles(files, path); AccessController.doPrivileged(new PrivilegedAction() { public Object run() { @@ -137,6 +143,8 @@ public class LWJGLInstaller { return null; } }); + + installed = true; } catch (Exception e) { LWJGLUtil.log("Failed extraction e = " + e.getMessage()); uninstall(); @@ -153,8 +161,73 @@ public class LWJGLInstaller { * installer, we can also be sure that the native libraries indeed are correct. * @throws Exception If we encounter a certificate mismatch */ - private static void validateCertificates() throws Exception { - /* TODO */ + private static HashMap validateCertificates() throws PrivilegedActionException { + InputStream is = LWJGLInstaller.class.getResourceAsStream(NATIVES_PLATFORM_JAR); + if(is == null) { + throw new PrivilegedActionException(new Exception("Unable to open " + NATIVES_PLATFORM_JAR + ", which was expected to be on the classpath")); + } + + // get our certificate chain + Certificate[] ownCerts = LWJGLInstaller.class.getProtectionDomain().getCodeSource().getCertificates(); + if(ownCerts == null || ownCerts.length == 0) { + throw new PrivilegedActionException(new Exception("Unable to get certificate chain for LWJGLInstaller")); + } + + // check that each of the entries in the jar is signed by same certificate as LWJGLInstaller + try { + HashMap files = new HashMap(); + JarInputStream jis = new JarInputStream(is); + + JarEntry native_entry = null; + while((native_entry = jis.getNextJarEntry()) != null) { + // skip directories and anything in directories + // conveniently ignores the manifest + if(native_entry.isDirectory() || native_entry.getName().indexOf('/') != -1) { + continue; + } + + // need to read the file, before the certificate is retrievable + // since we dont want to do two reads, we store it in memory for later use + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + copyFile(jis, baos, false); + files.put(native_entry.getName(), baos.toByteArray()); + + // now check the chain of an actual file + if(!validateCertificateChain(ownCerts, native_entry.getCertificates())) { + throw new Exception("Failed to validate certificate chain for " + native_entry.getName()); + } + } + + return files; + } catch (Exception e) { + throw new PrivilegedActionException(e); + } + } + + /** + * Validates the certificate chain for a single file + * @param ownCerts Chain of certificates to check against + * @param native_certs Chain of certificates to check + * @return true if the chains match + */ + private static boolean validateCertificateChain(Certificate[] ownCerts, Certificate[] native_certs) { + if(native_certs == null) { + LWJGLUtil.log("Unable to validate certificate chain. Native entry did not have a certificate chain at all"); + return false; + } + + if(ownCerts.length != native_certs.length) { + LWJGLUtil.log("Unable to validate certificate chain. Chain differs in length [" + ownCerts.length + " vs " + native_certs.length + "]"); + return false; + } + + for(int i=0; i 0) { os.write(COPY_BUFFER, 0, len); } - is.close(); + + if(closeInput) { + is.close(); + } os.close(); } @@ -229,8 +286,8 @@ public class LWJGLInstaller { return (String) AccessController.doPrivileged(new PrivilegedExceptionAction() { public Object run() throws Exception { // create the temp directory - File tempDir = new File(user_temp_dir + File.separator + "lwjgl-" + System.currentTimeMillis()); - if(!tempDir.mkdir()) { + File tempDir = new File(user_temp_dir + File.separator + ".lwjglinstall" + File.separator + System.currentTimeMillis()); + if(!tempDir.mkdirs()) { throw new IOException("Failed to create directory: " + tempDir); } @@ -238,6 +295,7 @@ public class LWJGLInstaller { // TODO: Write some info to the file ? File watermark = new File(tempDir.getAbsolutePath() + File.separator + ".lwjglinstaller"); watermark.createNewFile(); + watermark.deleteOnExit(); return tempDir.getAbsolutePath(); } }); @@ -267,14 +325,14 @@ public class LWJGLInstaller { AccessController.doPrivileged(new PrivilegedAction() { public Object run() { String temp = System.getProperty("java.io.tmpdir"); - File tempDir = new File(temp); + File tempDir = new File(temp + File.separator + MASTER_INSTALL_DIR); File[] files = tempDir.listFiles(new FileFilter() { /* * @see java.io.FileFilter#accept(java.io.File) */ public boolean accept(File pathname) { - return pathname.getAbsolutePath().indexOf("lwjgl") != -1 && isInstallDirectory(pathname); + return isStale(pathname); } /** @@ -283,9 +341,9 @@ public class LWJGLInstaller { * @param directory Directory to check * @return true if the directory is an install directory */ - private boolean isInstallDirectory(File directory) { + private boolean isStale(File directory) { File installFile = new File(directory.getAbsolutePath() + File.separator + ".lwjglinstaller"); - return installFile.exists(); + return !installFile.exists(); } });