added certificate check

reworked installer to expect a jar with the platform natives inside a jar
now installs into java.io.tmpdir/.lwjglinstall/<timestamp>
uninstaller checks for removed watermark file - tho it never gets removed *grumble*
This commit is contained in:
Brian Matzon 2006-07-03 23:22:46 +00:00
parent 26c6de6363
commit 5c82452d5e
1 changed files with 116 additions and 58 deletions

View File

@ -33,6 +33,8 @@ package org.lwjgl.util.applet;
import java.io.BufferedInputStream; import java.io.BufferedInputStream;
import java.io.BufferedOutputStream; import java.io.BufferedOutputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File; import java.io.File;
import java.io.FileFilter; import java.io.FileFilter;
import java.io.FileOutputStream; import java.io.FileOutputStream;
@ -41,9 +43,18 @@ import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
import java.security.AccessController; import java.security.AccessController;
import java.security.PrivilegedAction; import java.security.PrivilegedAction;
import java.security.PrivilegedActionException;
import java.security.PrivilegedExceptionAction; 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.LWJGLUtil;
import org.lwjgl.Sys;
/** /**
* <p> * <p>
@ -55,16 +66,6 @@ import org.lwjgl.LWJGLUtil;
*/ */
public class LWJGLInstaller { 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 */ /** Whether the installer has been called */
public static boolean installed; public static boolean installed;
@ -74,6 +75,12 @@ public class LWJGLInstaller {
/** Buffer used when copying files */ /** Buffer used when copying files */
private static final byte[] COPY_BUFFER = new byte[4096]; 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() { private LWJGLInstaller() {
/* Unused */ /* Unused */
} }
@ -99,11 +106,13 @@ public class LWJGLInstaller {
} }
try { try {
// libraries to validate and install // Validate the certificates of the platform native jar
String[] libraries = PLATFORM_FILES[LWJGLUtil.getPlatform() - 1]; HashMap files = (HashMap)
AccessController.doPrivileged(new PrivilegedExceptionAction() {
// Validate the certificates of the native files public Object run() throws PrivilegedActionException{
validateCertificates(); return validateCertificates();
}
});
// install shutdown installer hook // install shutdown installer hook
if(!disableUninstall) { if(!disableUninstall) {
@ -123,11 +132,8 @@ public class LWJGLInstaller {
String user_temp_dir = getPriviledgedString("java.io.tmpdir"); String user_temp_dir = getPriviledgedString("java.io.tmpdir");
final String path = createTemporaryDir(user_temp_dir); final String path = createTemporaryDir(user_temp_dir);
// extract natives // extract natives from jar
for (int i = 0; i < libraries.length; i++) { writeFiles(files, path);
String library = System.mapLibraryName(libraries[i]);
extract(library, path);
}
AccessController.doPrivileged(new PrivilegedAction() { AccessController.doPrivileged(new PrivilegedAction() {
public Object run() { public Object run() {
@ -137,6 +143,8 @@ public class LWJGLInstaller {
return null; return null;
} }
}); });
installed = true;
} catch (Exception e) { } catch (Exception e) {
LWJGLUtil.log("Failed extraction e = " + e.getMessage()); LWJGLUtil.log("Failed extraction e = " + e.getMessage());
uninstall(); uninstall();
@ -153,8 +161,73 @@ public class LWJGLInstaller {
* installer, we can also be sure that the native libraries indeed are correct. * installer, we can also be sure that the native libraries indeed are correct.
* @throws Exception If we encounter a certificate mismatch * @throws Exception If we encounter a certificate mismatch
*/ */
private static void validateCertificates() throws Exception { private static HashMap validateCertificates() throws PrivilegedActionException {
/* TODO */ 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<ownCerts.length; i++) {
if(!ownCerts[i].equals(native_certs[i])) {
LWJGLUtil.log("Certificate mismatch: " + ownCerts[i] + " != " + native_certs[i]);
return false;
}
}
return true;
} }
/** /**
@ -163,39 +236,20 @@ public class LWJGLInstaller {
* @param file File to extract * @param file File to extract
* @param path Path to extract to * @param path Path to extract to
*/ */
private static void extract(final String file, final String path) { private static void writeFiles(final HashMap files, final String path) {
AccessController.doPrivileged(new PrivilegedAction() { AccessController.doPrivileged(new PrivilegedAction() {
public Object run() { public Object run() {
// check for existing file, and get out
File out = new File(path + File.separator + file);
if (out.exists()) {
return null;
}
// create the new file and copy it to its destination
try { try {
out.createNewFile(); for(Iterator i = files.keySet().iterator(); i.hasNext();) {
String in = "/native/" + LWJGLUtil.getPlatformName() + "/" + file; String key = (String) i.next();
OutputStream os = new BufferedOutputStream(new FileOutputStream(out)); File out = new File(path + File.separator + key);
InputStream is = new BufferedInputStream(getClass().getResourceAsStream(in)); out.createNewFile();
InputStream is = new ByteArrayInputStream((byte[]) files.get(key));
// Sanity check OutputStream os = new BufferedOutputStream(new FileOutputStream(out));
// =========================================== copyFile(is, os, true);
if (os == null) {
LWJGLUtil.log("Unable to write to outputstream at " + out.getAbsolutePath());
return null;
} }
if (is == null) {
LWJGLUtil.log("Unable to read classpath inputstream from " + in);
return null;
}
// -------------------------------------------
// copy the actual file
copyFile(is, os);
} catch (IOException ioe) { } catch (IOException ioe) {
LWJGLUtil.log("Exception while extracting " + file + ": " + ioe.getMessage()); LWJGLUtil.log("Exception while extracting: " + ioe.getMessage());
return null; return null;
} }
return null; return null;
@ -209,12 +263,15 @@ public class LWJGLInstaller {
* @param os OutputStream to write to * @param os OutputStream to write to
* @throws IOException if the copy process fail in any way * @throws IOException if the copy process fail in any way
*/ */
static void copyFile(InputStream is, OutputStream os) throws IOException { static void copyFile(InputStream is, OutputStream os, boolean closeInput) throws IOException {
int len; int len;
while ((len = is.read(COPY_BUFFER)) > 0) { while ((len = is.read(COPY_BUFFER)) > 0) {
os.write(COPY_BUFFER, 0, len); os.write(COPY_BUFFER, 0, len);
} }
is.close();
if(closeInput) {
is.close();
}
os.close(); os.close();
} }
@ -229,8 +286,8 @@ public class LWJGLInstaller {
return (String) AccessController.doPrivileged(new PrivilegedExceptionAction() { return (String) AccessController.doPrivileged(new PrivilegedExceptionAction() {
public Object run() throws Exception { public Object run() throws Exception {
// create the temp directory // create the temp directory
File tempDir = new File(user_temp_dir + File.separator + "lwjgl-" + System.currentTimeMillis()); File tempDir = new File(user_temp_dir + File.separator + ".lwjglinstall" + File.separator + System.currentTimeMillis());
if(!tempDir.mkdir()) { if(!tempDir.mkdirs()) {
throw new IOException("Failed to create directory: " + tempDir); throw new IOException("Failed to create directory: " + tempDir);
} }
@ -238,6 +295,7 @@ public class LWJGLInstaller {
// TODO: Write some info to the file ? // TODO: Write some info to the file ?
File watermark = new File(tempDir.getAbsolutePath() + File.separator + ".lwjglinstaller"); File watermark = new File(tempDir.getAbsolutePath() + File.separator + ".lwjglinstaller");
watermark.createNewFile(); watermark.createNewFile();
watermark.deleteOnExit();
return tempDir.getAbsolutePath(); return tempDir.getAbsolutePath();
} }
}); });
@ -267,14 +325,14 @@ public class LWJGLInstaller {
AccessController.doPrivileged(new PrivilegedAction() { AccessController.doPrivileged(new PrivilegedAction() {
public Object run() { public Object run() {
String temp = System.getProperty("java.io.tmpdir"); 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() { File[] files = tempDir.listFiles(new FileFilter() {
/* /*
* @see java.io.FileFilter#accept(java.io.File) * @see java.io.FileFilter#accept(java.io.File)
*/ */
public boolean accept(File pathname) { 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 * @param directory Directory to check
* @return true if the directory is an install directory * @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"); File installFile = new File(directory.getAbsolutePath() + File.separator + ".lwjglinstaller");
return installFile.exists(); return !installFile.exists();
} }
}); });