prevent hijacking of signed Getdown to run malicious code; greater torrent download speed tolerance
This commit is contained in:
@@ -8,6 +8,7 @@
|
|||||||
-injars dist/getdown.jar(!**/tools/DigesterTask*,!**/tools/Differ*)
|
-injars dist/getdown.jar(!**/tools/DigesterTask*,!**/tools/Differ*)
|
||||||
-injars lib/jRegistryKey.jar(!META-INF/*)
|
-injars lib/jRegistryKey.jar(!META-INF/*)
|
||||||
-injars lib/samskivert.jar(!META-INF/*,!**/velocity/**,!**/xml/**)
|
-injars lib/samskivert.jar(!META-INF/*,!**/velocity/**,!**/xml/**)
|
||||||
|
-injars lib/commons-codec.jar(!META-INF/*)
|
||||||
-injars lib/commons-io.jar(!META-INF/*)
|
-injars lib/commons-io.jar(!META-INF/*)
|
||||||
-injars lib/snark.jar(!META-INF/*)
|
-injars lib/snark.jar(!META-INF/*)
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
samskivert.jar
|
samskivert.jar
|
||||||
ant.jar
|
ant.jar
|
||||||
|
commons-codec.jar
|
||||||
commons-io.jar
|
commons-io.jar
|
||||||
javaws.jar
|
javaws.jar
|
||||||
snark.jar
|
snark.jar
|
||||||
|
|||||||
@@ -23,6 +23,9 @@ package com.threerings.getdown.launcher;
|
|||||||
import java.awt.Container;
|
import java.awt.Container;
|
||||||
import java.awt.Image;
|
import java.awt.Image;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
|
import java.security.GeneralSecurityException;
|
||||||
|
import java.security.Signature;
|
||||||
|
import java.security.cert.Certificate;
|
||||||
|
|
||||||
import javax.swing.JApplet;
|
import javax.swing.JApplet;
|
||||||
import javax.swing.JPanel;
|
import javax.swing.JPanel;
|
||||||
@@ -32,6 +35,8 @@ import java.io.FileOutputStream;
|
|||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.PrintStream;
|
import java.io.PrintStream;
|
||||||
|
|
||||||
|
import org.apache.commons.codec.binary.Base64;
|
||||||
|
|
||||||
import com.samskivert.util.RunAnywhere;
|
import com.samskivert.util.RunAnywhere;
|
||||||
import com.samskivert.util.StringUtil;
|
import com.samskivert.util.StringUtil;
|
||||||
|
|
||||||
@@ -46,6 +51,54 @@ public class GetdownApplet extends JApplet
|
|||||||
@Override // documentation inherited
|
@Override // documentation inherited
|
||||||
public void init ()
|
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
|
// when run from an applet, we install
|
||||||
String root;
|
String root;
|
||||||
if (RunAnywhere.isWindows()) {
|
if (RunAnywhere.isWindows()) {
|
||||||
@@ -56,7 +109,6 @@ public class GetdownApplet extends JApplet
|
|||||||
root = ".getdown";
|
root = ".getdown";
|
||||||
}
|
}
|
||||||
|
|
||||||
String appname = getParameter("appname");
|
|
||||||
String appdir = System.getProperty("user.home") +
|
String appdir = System.getProperty("user.home") +
|
||||||
File.separator + root + File.separator + appname;
|
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
|
// if our getdown.txt file does not exist, auto-create it
|
||||||
File gdfile = new File(appDir, "getdown.txt");
|
File gdfile = new File(appDir, "getdown.txt");
|
||||||
if (!gdfile.exists()) {
|
if (!gdfile.exists()) {
|
||||||
String appbase = getParameter("appbase");
|
|
||||||
if (StringUtil.isBlank(appbase)) {
|
if (StringUtil.isBlank(appbase)) {
|
||||||
Log.warning("Missing 'appbase' cannot auto-create " +
|
Log.warning("Missing 'appbase' cannot auto-create " +
|
||||||
"application directory.");
|
"application directory.");
|
||||||
@@ -96,7 +147,6 @@ public class GetdownApplet extends JApplet
|
|||||||
}
|
}
|
||||||
|
|
||||||
// if a background image was specified, grabbit
|
// if a background image was specified, grabbit
|
||||||
String imgpath = getParameter("bgimage");
|
|
||||||
try {
|
try {
|
||||||
if (!StringUtil.isBlank(imgpath)) {
|
if (!StringUtil.isBlank(imgpath)) {
|
||||||
_bgimage = getImage(new URL(getDocumentBase(), imgpath));
|
_bgimage = getImage(new URL(getDocumentBase(), imgpath));
|
||||||
@@ -156,6 +206,10 @@ public class GetdownApplet extends JApplet
|
|||||||
@Override // documentation inherited
|
@Override // documentation inherited
|
||||||
public void start ()
|
public void start ()
|
||||||
{
|
{
|
||||||
|
if (!_safe) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
_getdown.start();
|
_getdown.start();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
@@ -188,4 +242,10 @@ public class GetdownApplet extends JApplet
|
|||||||
|
|
||||||
protected Getdown _getdown;
|
protected Getdown _getdown;
|
||||||
protected Image _bgimage;
|
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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -83,14 +83,15 @@ public class TorrentDownloader extends Downloader
|
|||||||
Snark snark = _torrentmap.get(rsrc);
|
Snark snark = _torrentmap.get(rsrc);
|
||||||
SnarkShutdown snarkStopper = _stoppermap.get(rsrc);
|
SnarkShutdown snarkStopper = _stoppermap.get(rsrc);
|
||||||
snark.collectPieces();
|
snark.collectPieces();
|
||||||
|
// Override the start time, since Snark allocates storage prior to
|
||||||
|
// doing any downloading
|
||||||
|
_start = System.currentTimeMillis();
|
||||||
while (_currentSize != snark.meta.getTotalLength()) {
|
while (_currentSize != snark.meta.getTotalLength()) {
|
||||||
long now = System.currentTimeMillis();
|
long now = System.currentTimeMillis();
|
||||||
if ((now - _lastUpdate) >= UPDATE_DELAY) {
|
if ((now - _lastUpdate) >= UPDATE_DELAY) {
|
||||||
_currentSize = snark.coordinator.getDownloaded();
|
_currentSize = snark.coordinator.getDownloaded();
|
||||||
if ((_currentSize < SIZE_THRESHOLD &&
|
if ((_currentSize < SIZE_THRESHOLD &&
|
||||||
(now - _start) >= TIME_THRESHOLD) ||
|
(now - _start) >= TIME_THRESHOLD)) {
|
||||||
(_currentSize == 0 &&
|
|
||||||
(now - _start) >= TIME_THRESHOLD / 4)) {
|
|
||||||
Log.info("Torrenting too slow, falling back to HTTP.");
|
Log.info("Torrenting too slow, falling back to HTTP.");
|
||||||
// The download isn't going as planned, abort;
|
// The download isn't going as planned, abort;
|
||||||
snarkStopper.run();
|
snarkStopper.run();
|
||||||
|
|||||||
@@ -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(
|
||||||
|
"<param name=\"appbase\" value=\"" + appbase + "\" />");
|
||||||
|
System.out.println(
|
||||||
|
"<param name=\"appname\" value=\"" + appname + "\" />");
|
||||||
|
System.out.println(
|
||||||
|
"<param name=\"bgimage\" value=\"" + imgpath + "\" />");
|
||||||
|
System.out.println(
|
||||||
|
"<param name=\"signature\" value=\"" + signed + "\" />");
|
||||||
|
} catch (Exception e) {
|
||||||
|
System.err.println("Failed to produce signature.");
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user