From c92e372d39b379455a4f8346e48d47a60dec2b18 Mon Sep 17 00:00:00 2001 From: Elizabeth Fong Date: Sun, 20 Aug 2006 09:45:21 +0000 Subject: [PATCH] prevent hijacking of signed Getdown to run malicious code; greater torrent download speed tolerance --- etc/getdown.pro | 1 + lib/LIBS | 1 + .../getdown/launcher/GetdownApplet.java | 66 ++++++++++++++++++- .../getdown/launcher/TorrentDownloader.java | 7 +- .../getdown/tools/AppletParamSigner.java | 59 +++++++++++++++++ 5 files changed, 128 insertions(+), 6 deletions(-) create mode 100644 src/java/com/threerings/getdown/tools/AppletParamSigner.java diff --git a/etc/getdown.pro b/etc/getdown.pro index a8fb173..aac5920 100644 --- a/etc/getdown.pro +++ b/etc/getdown.pro @@ -8,6 +8,7 @@ -injars dist/getdown.jar(!**/tools/DigesterTask*,!**/tools/Differ*) -injars lib/jRegistryKey.jar(!META-INF/*) -injars lib/samskivert.jar(!META-INF/*,!**/velocity/**,!**/xml/**) +-injars lib/commons-codec.jar(!META-INF/*) -injars lib/commons-io.jar(!META-INF/*) -injars lib/snark.jar(!META-INF/*) diff --git a/lib/LIBS b/lib/LIBS index ff566c1..e2f70ea 100644 --- a/lib/LIBS +++ b/lib/LIBS @@ -1,5 +1,6 @@ samskivert.jar ant.jar +commons-codec.jar commons-io.jar javaws.jar snark.jar diff --git a/src/java/com/threerings/getdown/launcher/GetdownApplet.java b/src/java/com/threerings/getdown/launcher/GetdownApplet.java index 9c4a454..a8220c8 100644 --- a/src/java/com/threerings/getdown/launcher/GetdownApplet.java +++ b/src/java/com/threerings/getdown/launcher/GetdownApplet.java @@ -23,6 +23,9 @@ package com.threerings.getdown.launcher; import java.awt.Container; import java.awt.Image; import java.net.URL; +import java.security.GeneralSecurityException; +import java.security.Signature; +import java.security.cert.Certificate; import javax.swing.JApplet; import javax.swing.JPanel; @@ -32,6 +35,8 @@ import java.io.FileOutputStream; import java.io.File; import java.io.PrintStream; +import org.apache.commons.codec.binary.Base64; + import com.samskivert.util.RunAnywhere; import com.samskivert.util.StringUtil; @@ -46,6 +51,54 @@ public class GetdownApplet extends JApplet @Override // documentation inherited public void init () { + // First off, verify that we are not being hijacked to execute + // malicious code in the name of the signer. + String appbase = getParameter("appbase"); + String appname = getParameter("appname"); + String imgpath = getParameter("bgimage"); + if (appbase == null) { + appbase = ""; + } + if (appname == null) { + appname = ""; + } + if (imgpath == null) { + imgpath = ""; + } + String params = appbase + appname + imgpath; + String signature = getParameter("signature"); + if (signature == null) { + signature = ""; + } + + Object[] signers = GetdownApplet.class.getSigners(); + if (signers.length == 0) { + _safe = true; + } + for (Object signer : signers) { + if (!_safe && signer instanceof Certificate) { + Certificate cert = (Certificate)signer; + try { + Signature sig = Signature.getInstance("SHA1withRSA"); + sig.initVerify(cert); + Log.info(params); + sig.update(params.getBytes()); + byte[] rawsig = Base64.decodeBase64(signature.getBytes()); + if (sig.verify(rawsig)) { + _safe = true; + } + } catch (GeneralSecurityException gse) { + // ignore the error - the default is to not launch. + } + } + } + + if (!_safe) { + Log.warning("Signed getdown invoked on unsigned application; " + + "aborting installation."); + return; + } + // when run from an applet, we install String root; if (RunAnywhere.isWindows()) { @@ -56,7 +109,6 @@ public class GetdownApplet extends JApplet root = ".getdown"; } - String appname = getParameter("appname"); String appdir = System.getProperty("user.home") + File.separator + root + File.separator + appname; @@ -82,7 +134,6 @@ public class GetdownApplet extends JApplet // if our getdown.txt file does not exist, auto-create it File gdfile = new File(appDir, "getdown.txt"); if (!gdfile.exists()) { - String appbase = getParameter("appbase"); if (StringUtil.isBlank(appbase)) { Log.warning("Missing 'appbase' cannot auto-create " + "application directory."); @@ -96,7 +147,6 @@ public class GetdownApplet extends JApplet } // if a background image was specified, grabbit - String imgpath = getParameter("bgimage"); try { if (!StringUtil.isBlank(imgpath)) { _bgimage = getImage(new URL(getDocumentBase(), imgpath)); @@ -156,6 +206,10 @@ public class GetdownApplet extends JApplet @Override // documentation inherited public void start () { + if (!_safe) { + return; + } + try { _getdown.start(); } catch (Exception e) { @@ -188,4 +242,10 @@ public class GetdownApplet extends JApplet protected Getdown _getdown; protected Image _bgimage; + + /** + * Getdown will refuse to initialize if the jar is signed but the + * parameters are not validated to prevent malicious code from being run. + */ + protected boolean _safe = false; } diff --git a/src/java/com/threerings/getdown/launcher/TorrentDownloader.java b/src/java/com/threerings/getdown/launcher/TorrentDownloader.java index 25a45e5..dcb6cc4 100644 --- a/src/java/com/threerings/getdown/launcher/TorrentDownloader.java +++ b/src/java/com/threerings/getdown/launcher/TorrentDownloader.java @@ -83,14 +83,15 @@ public class TorrentDownloader extends Downloader Snark snark = _torrentmap.get(rsrc); SnarkShutdown snarkStopper = _stoppermap.get(rsrc); snark.collectPieces(); + // Override the start time, since Snark allocates storage prior to + // doing any downloading + _start = System.currentTimeMillis(); while (_currentSize != snark.meta.getTotalLength()) { long now = System.currentTimeMillis(); if ((now - _lastUpdate) >= UPDATE_DELAY) { _currentSize = snark.coordinator.getDownloaded(); if ((_currentSize < SIZE_THRESHOLD && - (now - _start) >= TIME_THRESHOLD) || - (_currentSize == 0 && - (now - _start) >= TIME_THRESHOLD / 4)) { + (now - _start) >= TIME_THRESHOLD)) { Log.info("Torrenting too slow, falling back to HTTP."); // The download isn't going as planned, abort; snarkStopper.run(); diff --git a/src/java/com/threerings/getdown/tools/AppletParamSigner.java b/src/java/com/threerings/getdown/tools/AppletParamSigner.java new file mode 100644 index 0000000..202fe47 --- /dev/null +++ b/src/java/com/threerings/getdown/tools/AppletParamSigner.java @@ -0,0 +1,59 @@ +package com.threerings.getdown.tools; + +import java.io.BufferedInputStream; +import java.io.FileInputStream; +import java.security.KeyStore; +import java.security.PrivateKey; +import java.security.Signature; + +import org.apache.commons.codec.binary.Base64; + +/** + * Produces a signed hash of the appbase, appname, and image path to ensure + * that signed copies of Getdown are not hijacked to run malicious code. + */ +public class AppletParamSigner +{ + public static void main (String[] args) + { + try { + if (args.length != 7) { + System.err.println( + "AppletParamSigner keystore storepass alias keypass " + + "appbase appname imgpath" + ); + System.exit(255); + } + String keystore = args[0]; + String storepass = args[1]; + String alias = args[2]; + String keypass = args[3]; + String appbase = args[4]; + String appname = args[5]; + String imgpath = args[6]; + String params = appbase + appname + imgpath; + + KeyStore store = KeyStore.getInstance("JKS"); + store.load(new BufferedInputStream(new FileInputStream(keystore)), + storepass.toCharArray()); + PrivateKey key = (PrivateKey)store.getKey(alias, + keypass.toCharArray()); + Signature sig = Signature.getInstance("SHA1withRSA"); + sig.initSign(key); + sig.update(params.getBytes()); + String signed = new String(Base64.encodeBase64(sig.sign())); + System.out.println( + ""); + System.out.println( + ""); + System.out.println( + ""); + System.out.println( + ""); + } catch (Exception e) { + System.err.println("Failed to produce signature."); + e.printStackTrace(); + } + } + +}