You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@lucene.apache.org by no...@apache.org on 2015/03/09 13:23:59 UTC

svn commit: r1665207 - in /lucene/dev/trunk/solr: ./ core/src/java/org/apache/solr/cloud/ core/src/java/org/apache/solr/core/ core/src/java/org/apache/solr/util/ core/src/test/org/apache/solr/cloud/ core/src/test/org/apache/solr/core/

Author: noble
Date: Mon Mar  9 12:23:59 2015
New Revision: 1665207

URL: http://svn.apache.org/r1665207
Log:
SOLR-7126: Secure loading of runtime external jars

Modified:
    lucene/dev/trunk/solr/CHANGES.txt
    lucene/dev/trunk/solr/core/src/java/org/apache/solr/cloud/CloudUtil.java
    lucene/dev/trunk/solr/core/src/java/org/apache/solr/core/JarRepository.java
    lucene/dev/trunk/solr/core/src/java/org/apache/solr/core/MemClassLoader.java
    lucene/dev/trunk/solr/core/src/java/org/apache/solr/core/PluginBag.java
    lucene/dev/trunk/solr/core/src/java/org/apache/solr/util/CryptoKeys.java
    lucene/dev/trunk/solr/core/src/test/org/apache/solr/cloud/TestCryptoKeys.java
    lucene/dev/trunk/solr/core/src/test/org/apache/solr/core/TestDynamicLoading.java

Modified: lucene/dev/trunk/solr/CHANGES.txt
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/solr/CHANGES.txt?rev=1665207&r1=1665206&r2=1665207&view=diff
==============================================================================
--- lucene/dev/trunk/solr/CHANGES.txt (original)
+++ lucene/dev/trunk/solr/CHANGES.txt Mon Mar  9 12:23:59 2015
@@ -156,6 +156,8 @@ New Features
   cluster is not ready, and can now take an optional timeout argument to wait
   for the cluster. (Alan Woodward, shalin, yonik, Mark Miller, Vitaliy Zhovtyuk)
 
+* SOLR-7126: Secure loading of runtime external jars (Noble Paul)
+
 Bug Fixes
 ----------------------
 

Modified: lucene/dev/trunk/solr/core/src/java/org/apache/solr/cloud/CloudUtil.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/solr/core/src/java/org/apache/solr/cloud/CloudUtil.java?rev=1665207&r1=1665206&r2=1665207&view=diff
==============================================================================
--- lucene/dev/trunk/solr/core/src/java/org/apache/solr/cloud/CloudUtil.java (original)
+++ lucene/dev/trunk/solr/core/src/java/org/apache/solr/cloud/CloudUtil.java Mon Mar  9 12:23:59 2015
@@ -21,7 +21,6 @@ import java.io.File;
 import java.io.IOException;
 import java.util.Collections;
 import java.util.HashMap;
-import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
 
@@ -109,12 +108,13 @@ public class CloudUtil {
   /**Read the list of public keys from ZK
    */
 
-  public static Map<String, byte[]> getTrustedKeys(SolrZkClient zk){
-     Map<String,byte[]> result =  new HashMap<>();
+  public static Map<String, byte[]> getTrustedKeys(SolrZkClient zk, String dir) {
+    Map<String, byte[]> result = new HashMap<>();
     try {
-      List<String> children = zk.getChildren("/keys", null, true);
+      List<String> children = zk.getChildren("/keys/" + dir, null, true);
       for (String key : children) {
-        result.put(key, zk.getData("/keys/"+key,null,null,true));
+        if (key.endsWith(".der")) result.put(key, zk.getData("/keys/" + dir +
+            "/" + key, null, null, true));
       }
     } catch (KeeperException.NoNodeException e) {
       log.warn("Error fetching key names");
@@ -128,4 +128,5 @@ public class CloudUtil {
     return result;
 
   }
+
 }

Modified: lucene/dev/trunk/solr/core/src/java/org/apache/solr/core/JarRepository.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/solr/core/src/java/org/apache/solr/core/JarRepository.java?rev=1665207&r1=1665206&r2=1665207&view=diff
==============================================================================
--- lucene/dev/trunk/solr/core/src/java/org/apache/solr/core/JarRepository.java (original)
+++ lucene/dev/trunk/solr/core/src/java/org/apache/solr/core/JarRepository.java Mon Mar  9 12:23:59 2015
@@ -44,6 +44,7 @@ import org.apache.solr.common.cloud.Repl
 import org.apache.solr.common.cloud.Slice;
 import org.apache.solr.common.cloud.ZkStateReader;
 import org.apache.solr.handler.admin.CollectionsHandler;
+import org.apache.solr.util.CryptoKeys;
 import org.apache.solr.util.SimplePostTool;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -204,6 +205,10 @@ public class JarRepository {
       return null;
     }
 
+    public String checkSignature(String base64Sig, CryptoKeys keys) {
+      return keys.verify(base64Sig, buffer);
+    }
+
   }
 
   public static class JarContentRef {

Modified: lucene/dev/trunk/solr/core/src/java/org/apache/solr/core/MemClassLoader.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/solr/core/src/java/org/apache/solr/core/MemClassLoader.java?rev=1665207&r1=1665206&r2=1665207&view=diff
==============================================================================
--- lucene/dev/trunk/solr/core/src/java/org/apache/solr/core/MemClassLoader.java (original)
+++ lucene/dev/trunk/solr/core/src/java/org/apache/solr/core/MemClassLoader.java Mon Mar  9 12:23:59 2015
@@ -57,6 +57,7 @@ public class MemClassLoader extends Clas
     for (PluginBag.RuntimeLib lib : libs) {
       try {
         lib.loadJar();
+        lib.verify();
       } catch (Exception exception) {
         if (exception instanceof SolrException) throw (SolrException) exception;
         throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, "Atleast one runtimeLib could not be loaded", exception);
@@ -116,7 +117,7 @@ public class MemClassLoader extends Clas
       try {
         buf = lib.getFileContent(path);
         if (buf != null) {
-          jarName.set(lib.name);
+          jarName.set(lib.getName());
           break;
         }
       } catch (Exception exp) {

Modified: lucene/dev/trunk/solr/core/src/java/org/apache/solr/core/PluginBag.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/solr/core/src/java/org/apache/solr/core/PluginBag.java?rev=1665207&r1=1665206&r2=1665207&view=diff
==============================================================================
--- lucene/dev/trunk/solr/core/src/java/org/apache/solr/core/PluginBag.java (original)
+++ lucene/dev/trunk/solr/core/src/java/org/apache/solr/core/PluginBag.java Mon Mar  9 12:23:59 2015
@@ -20,6 +20,7 @@ package org.apache.solr.core;
 
 import java.io.IOException;
 import java.nio.ByteBuffer;
+import java.text.MessageFormat;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.HashMap;
@@ -29,10 +30,12 @@ import java.util.Set;
 
 import org.apache.lucene.analysis.util.ResourceLoader;
 import org.apache.lucene.analysis.util.ResourceLoaderAware;
+import org.apache.solr.cloud.CloudUtil;
 import org.apache.solr.common.SolrException;
 import org.apache.solr.handler.RequestHandlerBase;
 import org.apache.solr.handler.component.SearchComponent;
 import org.apache.solr.request.SolrRequestHandler;
+import org.apache.solr.util.CryptoKeys;
 import org.apache.solr.util.plugin.NamedListInitializedPlugin;
 import org.apache.solr.util.plugin.PluginInfoInitialized;
 import org.apache.solr.util.plugin.SolrCoreAware;
@@ -323,10 +326,10 @@ public class PluginBag<T> implements Aut
    * This represents a Runtime Jar. A jar requires two details , name and version
    */
   public static class RuntimeLib implements PluginInfoInitialized, AutoCloseable {
-    String name;
-    String version;
+    private String name, version, sig;
     private JarRepository.JarContentRef jarContent;
-    private final JarRepository jarRepository;
+    private final CoreContainer coreContainer;
+    private boolean verified = false;
 
     @Override
     public void init(PluginInfo info) {
@@ -336,10 +339,11 @@ public class PluginBag<T> implements Aut
         throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, "runtimeLib must have name and version");
       }
       version = String.valueOf(v);
+      sig = info.attributes.get("sig");
     }
 
     public RuntimeLib(SolrCore core) {
-      jarRepository = core.getCoreDescriptor().getCoreContainer().getJarRepository();
+      coreContainer = core.getCoreDescriptor().getCoreContainer();
     }
 
 
@@ -347,10 +351,23 @@ public class PluginBag<T> implements Aut
       if (jarContent != null) return;
       synchronized (this) {
         if (jarContent != null) return;
-        jarContent = jarRepository.getJarIncRef(name + "/" + version);
+        jarContent = coreContainer.getJarRepository().getJarIncRef(name + "/" + version);
       }
     }
 
+    public String getName() {
+      return name;
+    }
+
+    public String getVersion() {
+      return version;
+    }
+
+    public String getSig() {
+      return sig;
+
+    }
+
     public ByteBuffer getFileContent(String entryName) throws IOException {
       if (jarContent == null)
         throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, "jar not available: " + name + "/" + version);
@@ -360,7 +377,7 @@ public class PluginBag<T> implements Aut
 
     @Override
     public void close() throws Exception {
-      if (jarContent != null) jarRepository.decrementJarRefCount(jarContent);
+      if (jarContent != null) coreContainer.getJarRepository().decrementJarRefCount(jarContent);
     }
 
     public static List<RuntimeLib> getLibObjects(SolrCore core, List<PluginInfo> libs) {
@@ -372,5 +389,38 @@ public class PluginBag<T> implements Aut
       }
       return l;
     }
+
+    public void verify() throws Exception {
+      if (verified) return;
+      if (jarContent == null) {
+        log.error("Calling verify before loading the jar");
+        return;
+      }
+
+      if (!coreContainer.isZooKeeperAware())
+        throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, "Signing jar is possible only in cloud");
+      Map<String, byte[]> keys = CloudUtil.getTrustedKeys(coreContainer.getZkController().getZkClient(), "exe");
+      if (keys.isEmpty()) {
+        if (sig == null) {
+          verified = true;
+          log.info("A run time lib {} is loaded  without verification ", name);
+          return;
+        } else {
+          throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, "No public keys are available in ZK to verify signature for runtime lib  " + name);
+        }
+      } else if (sig == null) {
+        throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, MessageFormat.format("runtimelib {0} should be signed with one of the keys in ZK /keys/exe ", name));
+      }
+
+      try {
+        String matchedKey = jarContent.jar.checkSignature(sig, new CryptoKeys(keys));
+        if (matchedKey == null)
+          throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, "No key matched signature for jar : " + name + " version: " + version);
+        log.info("Jar {} signed with {} successfully verified", name, matchedKey);
+      } catch (Exception e) {
+        if (e instanceof SolrException) throw e;
+        throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, "Error verifying key ", e);
+      }
+    }
   }
 }

Modified: lucene/dev/trunk/solr/core/src/java/org/apache/solr/util/CryptoKeys.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/solr/core/src/java/org/apache/solr/util/CryptoKeys.java?rev=1665207&r1=1665206&r2=1665207&view=diff
==============================================================================
--- lucene/dev/trunk/solr/core/src/java/org/apache/solr/util/CryptoKeys.java (original)
+++ lucene/dev/trunk/solr/core/src/java/org/apache/solr/util/CryptoKeys.java Mon Mar  9 12:23:59 2015
@@ -37,9 +37,8 @@ import org.slf4j.LoggerFactory;
  */
 public final class CryptoKeys {
   private static final Logger log = LoggerFactory.getLogger(CryptoKeys.class);
-
   private final Map<String, PublicKey> keys;
-
+  private Exception exception;
 
   public CryptoKeys(Map<String, byte[]> trustedKeys) throws Exception {
     HashMap<String, PublicKey> m = new HashMap<>();
@@ -53,15 +52,16 @@ public final class CryptoKeys {
   /**
    * Try with all signatures and return the name of the signature that matched
    */
-  public String verify(String sig, byte[] data) {
-
+  public String verify(String sig, ByteBuffer data) {
+    exception = null;
     for (Map.Entry<String, PublicKey> entry : keys.entrySet()) {
       boolean verified;
       try {
-        verified = CryptoKeys.verify(entry.getValue(), Base64.base64ToByteArray(sig), ByteBuffer.wrap(data));
+        verified = CryptoKeys.verify(entry.getValue(), Base64.base64ToByteArray(sig), data);
         log.info("verified {} ", verified);
         if (verified) return entry.getKey();
       } catch (Exception e) {
+        exception = e;
         log.info("NOT verified  ");
       }
 
@@ -89,6 +89,7 @@ public final class CryptoKeys {
    * @param data      The data tha is signed
    */
   public static boolean verify(PublicKey publicKey, byte[] sig, ByteBuffer data) throws InvalidKeyException, SignatureException {
+    int oldPos = data.position();
     Signature signature = null;
     try {
       signature = Signature.getInstance("SHA1withRSA");
@@ -99,6 +100,9 @@ public final class CryptoKeys {
 
     } catch (NoSuchAlgorithmException e) {
       //will not happen
+    } finally {
+      //Signature.update resets the position. set it back to old
+      data.position(oldPos);
     }
     return false;
   }

Modified: lucene/dev/trunk/solr/core/src/test/org/apache/solr/cloud/TestCryptoKeys.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/solr/core/src/test/org/apache/solr/cloud/TestCryptoKeys.java?rev=1665207&r1=1665206&r2=1665207&view=diff
==============================================================================
--- lucene/dev/trunk/solr/core/src/test/org/apache/solr/cloud/TestCryptoKeys.java (original)
+++ lucene/dev/trunk/solr/core/src/test/org/apache/solr/cloud/TestCryptoKeys.java Mon Mar  9 12:23:59 2015
@@ -19,18 +19,56 @@ package org.apache.solr.cloud;
 
 import java.io.FileInputStream;
 import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
 import java.util.Map;
 
+import org.apache.solr.client.solrj.SolrClient;
+import org.apache.solr.client.solrj.impl.HttpSolrClient;
 import org.apache.solr.common.cloud.SolrZkClient;
+import org.apache.solr.core.ConfigOverlay;
+import org.apache.solr.core.MemClassLoader;
+import org.apache.solr.core.TestDynamicLoading;
+import org.apache.solr.core.TestSolrConfigHandler;
+import org.apache.solr.handler.TestBlobHandler;
 import org.apache.solr.util.CryptoKeys;
+import org.apache.solr.util.RESTfulServerProvider;
+import org.apache.solr.util.RestTestHarness;
 import org.apache.zookeeper.CreateMode;
-import org.apache.zookeeper.KeeperException;
+import org.junit.BeforeClass;
 import org.junit.Test;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import static java.util.Arrays.asList;
+import static org.apache.solr.handler.TestSolrConfigHandlerCloud.compareValues;
+
 public class TestCryptoKeys extends AbstractFullDistribZkTestBase {
   private static final Logger logger = LoggerFactory.getLogger(TestCryptoKeys.class);
+  private List<RestTestHarness> restTestHarnesses = new ArrayList<>();
+
+  private void setupHarnesses() {
+    for (final SolrClient client : clients) {
+      RestTestHarness harness = new RestTestHarness(new RESTfulServerProvider() {
+        @Override
+        public String getBaseURL() {
+          return ((HttpSolrClient) client).getBaseURL();
+        }
+      });
+      restTestHarnesses.add(harness);
+    }
+  }
+
+
+  @Override
+  public void distribTearDown() throws Exception {
+    super.distribTearDown();
+    for (RestTestHarness r : restTestHarnesses) {
+      r.close();
+    }
+  }
 
 
   @Override
@@ -46,30 +84,27 @@ public class TestCryptoKeys extends Abst
 
   @Test
   public void test() throws Exception {
+    System.setProperty("enable.runtime.lib", "true");
+    setupHarnesses();
     String pk1sig = "G8LEW7uJ1is81Aqqfl3Sld3qDtOxPuVFeTLJHFJWecgDvUkmJNFXmf7nkHOVlXnDWahp1vqZf0W02VHXg37lBw==";
     String pk2sig = "pCyBQycB/0YvLVZfKLDIIqG1tFwM/awqzkp2QNpO7R3ThTqmmrj11wEJFDRLkY79efuFuQPHt40EE7jrOKoj9jLNELsfEqvU3jw9sZKiDONY+rV9Bj9QPeW8Pgt+F9Y1";
     String wrongKeySig = "xTk2hTipfpb+J5s4x3YZGOXkmHWtnJz05Vvd8RTm/Q1fbQVszR7vMk6dQ1URxX08fcg4HvxOo8g9bG2TSMOGjg==";
     String result = null;
     CryptoKeys cryptoKeys = null;
     SolrZkClient zk = getCommonCloudSolrClient().getZkStateReader().getZkClient();
-    cryptoKeys = new CryptoKeys(CloudUtil.getTrustedKeys(zk));
-    byte[] samplefile = readFile("samplefile.bin");
-    //there are no keys
+    cryptoKeys = new CryptoKeys(CloudUtil.getTrustedKeys(zk, "exe"));
+    ByteBuffer samplefile = ByteBuffer.wrap(readFile("cryptokeys/samplefile.bin"));
+    //there are no keys yet created in ZK
 
     result = cryptoKeys.verify( pk1sig,samplefile);
     assertNull(result);
 
-    zk.makePath("/keys", true);
-
-    createNode(zk, "pubk1.der");
-    createNode(zk, "pubk2.der");
-
-    Map<String, byte[]> trustedKeys = CloudUtil.getTrustedKeys(zk);
-
+    zk.makePath("/keys/exe", true);
+    zk.create("/keys/exe/pubk1.der", readFile("cryptokeys/pubk1.der"), CreateMode.PERSISTENT, true);
+    zk.create("/keys/exe/pubk2.der", readFile("cryptokeys/pubk2.der"), CreateMode.PERSISTENT, true);
+    Map<String, byte[]> trustedKeys = CloudUtil.getTrustedKeys(zk, "exe");
 
     cryptoKeys = new CryptoKeys(trustedKeys);
-
-
     result = cryptoKeys.verify(pk2sig, samplefile);
     assertEquals("pubk2.der", result);
 
@@ -83,30 +118,123 @@ public class TestCryptoKeys extends Abst
     } catch (Exception e) {
       //pass
     }
-
-
     try {
       result = cryptoKeys.verify( "SGVsbG8gV29ybGQhCg==", samplefile);
       assertNull(result);
     } catch (Exception e) {
       //pass
-
     }
 
 
+    HttpSolrClient randomClient = (HttpSolrClient) clients.get(random().nextInt(clients.size()));
+    String baseURL = randomClient.getBaseURL();
+    baseURL = baseURL.substring(0, baseURL.lastIndexOf('/'));
+
+    TestBlobHandler.createSystemCollection(new HttpSolrClient(baseURL, randomClient.getHttpClient()));
+    waitForRecoveriesToFinish(".system", true);
+
+    ByteBuffer jar = TestDynamicLoading.getFileContent("runtimecode/runtimelibs.jar.bin");
+    String blobName = "signedjar";
+    TestBlobHandler.postAndCheck(cloudClient, baseURL, blobName, jar, 1);
+
+    String payload = "{\n" +
+        "'create-requesthandler' : { 'name' : '/runtime', 'class': 'org.apache.solr.core.RuntimeLibReqHandler' , 'runtimeLib':true }" +
+        "}";
+    RestTestHarness client = restTestHarnesses.get(random().nextInt(restTestHarnesses.size()));
+    TestSolrConfigHandler.runConfigCommand(client, "/config?wt=json", payload);
+
+    TestSolrConfigHandler.testForResponseElement(client,
+        null,
+        "/config/overlay?wt=json",
+        null,
+        Arrays.asList("overlay", "requestHandler", "/runtime", "class"),
+        "org.apache.solr.core.RuntimeLibReqHandler", 10);
+
+
+    payload = "{\n" +
+        "'add-runtimelib' : { 'name' : 'signedjar' ,'version':1}\n" +
+        "}";
+    client = restTestHarnesses.get(random().nextInt(restTestHarnesses.size()));
+    TestSolrConfigHandler.runConfigCommand(client, "/config?wt=json", payload);
+    TestSolrConfigHandler.testForResponseElement(client,
+        null,
+        "/config/overlay?wt=json",
+        null,
+        Arrays.asList("overlay", "runtimeLib", blobName, "version"),
+        1l, 10);
+
+    Map map = TestSolrConfigHandler.getRespMap("/runtime?wt=json", client);
+    String s = (String) ConfigOverlay.getObjectByPath(map, false, Arrays.asList("error", "msg"));
+    assertNotNull(TestBlobHandler.getAsString(map), s);
+    assertTrue(TestBlobHandler.getAsString(map), s.contains("should be signed with one of the keys in ZK /keys/exe"));
+
+    String wrongSig = "QKqHtd37QN02iMW9UEgvAO9g9qOOuG5vEBNkbUsN7noc2hhXKic/ABFIOYJA9PKw61mNX2EmNFXOcO3WClYdSw==";
+
+    payload = "{\n" +
+        "'update-runtimelib' : { 'name' : 'signedjar' ,'version':1, 'sig': 'QKqHtd37QN02iMW9UEgvAO9g9qOOuG5vEBNkbUsN7noc2hhXKic/ABFIOYJA9PKw61mNX2EmNFXOcO3WClYdSw=='}\n" +
+        "}";
+    client = restTestHarnesses.get(random().nextInt(restTestHarnesses.size()));
+    TestSolrConfigHandler.runConfigCommand(client, "/config?wt=json", payload);
+    TestSolrConfigHandler.testForResponseElement(client,
+        null,
+        "/config/overlay?wt=json",
+        null,
+        Arrays.asList("overlay", "runtimeLib", blobName, "sig"),
+        wrongSig, 10);
+
+    map = TestSolrConfigHandler.getRespMap("/runtime?wt=json", client);
+    s = (String) ConfigOverlay.getObjectByPath(map, false, Arrays.asList("error", "msg"));
+    assertNotNull(TestBlobHandler.getAsString(map), s);//No key matched signature for jar
+    assertTrue(TestBlobHandler.getAsString(map), s.contains("No key matched signature for jar"));
+
+    String rightSig = "YkTQgOtvcM/H/5EQdABGl3wjjrPhonAGlouIx59vppBy2cZEofX3qX1yZu5sPNRmJisNXEuhHN2149dxeUmk2Q==";
+
+    payload = "{\n" +
+        "'update-runtimelib' : { 'name' : 'signedjar' ,'version':1, 'sig': 'YkTQgOtvcM/H/5EQdABGl3wjjrPhonAGlouIx59vppBy2cZEofX3qX1yZu5sPNRmJisNXEuhHN2149dxeUmk2Q=='}\n" +
+        "}";
+    client = restTestHarnesses.get(random().nextInt(restTestHarnesses.size()));
+    TestSolrConfigHandler.runConfigCommand(client, "/config?wt=json", payload);
+    TestSolrConfigHandler.testForResponseElement(client,
+        null,
+        "/config/overlay?wt=json",
+        null,
+        Arrays.asList("overlay", "runtimeLib", blobName, "sig"),
+        rightSig, 10);
+
+    map = TestSolrConfigHandler.testForResponseElement(client,
+        null,
+        "/runtime?wt=json",
+        null,
+        Arrays.asList("class"),
+        "org.apache.solr.core.RuntimeLibReqHandler", 10);
+    compareValues(map, MemClassLoader.class.getName(), asList("loader"));
+
+    rightSig = "VJPMTxDf8Km3IBj2B5HWkIOqeM/o+HHNobOYCNA3WjrEVfOMZbMMqS1Lo7uLUUp//RZwOGkOhrUhuPNY1z2CGEIKX2/m8VGH64L14d52oSvFiwhoTDDuuyjW1TFGu35D";
+    payload = "{\n" +
+        "'update-runtimelib' : { 'name' : 'signedjar' ,'version':1, 'sig': 'VJPMTxDf8Km3IBj2B5HWkIOqeM/o+HHNobOYCNA3WjrEVfOMZbMMqS1Lo7uLUUp//RZwOGkOhrUhuPNY1z2CGEIKX2/m8VGH64L14d52oSvFiwhoTDDuuyjW1TFGu35D'}\n" +
+        "}";
+    client = restTestHarnesses.get(random().nextInt(restTestHarnesses.size()));
+    TestSolrConfigHandler.runConfigCommand(client, "/config?wt=json", payload);
+    TestSolrConfigHandler.testForResponseElement(client,
+        null,
+        "/config/overlay?wt=json",
+        null,
+        Arrays.asList("overlay", "runtimeLib", blobName, "sig"),
+        rightSig, 10);
+
+    map = TestSolrConfigHandler.testForResponseElement(client,
+        null,
+        "/runtime?wt=json",
+        null,
+        Arrays.asList("class"),
+        "org.apache.solr.core.RuntimeLibReqHandler", 10);
+    compareValues(map, MemClassLoader.class.getName(), asList("loader"));
   }
 
 
-
-  private void createNode(SolrZkClient zk, String fname) throws IOException, KeeperException, InterruptedException {
-    byte[] buf = readFile(fname);
-    zk.create("/keys/" + fname, buf, CreateMode.PERSISTENT, true);
-
-  }
-
   private byte[] readFile(String fname) throws IOException {
     byte[] buf = null;
-    try (FileInputStream fis = new FileInputStream(getFile("cryptokeys/" + fname))) {
+    try (FileInputStream fis = new FileInputStream(getFile(fname))) {
       buf = new byte[fis.available()];
       fis.read(buf);
     }

Modified: lucene/dev/trunk/solr/core/src/test/org/apache/solr/core/TestDynamicLoading.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/solr/core/src/test/org/apache/solr/core/TestDynamicLoading.java?rev=1665207&r1=1665206&r2=1665207&view=diff
==============================================================================
--- lucene/dev/trunk/solr/core/src/test/org/apache/solr/core/TestDynamicLoading.java (original)
+++ lucene/dev/trunk/solr/core/src/test/org/apache/solr/core/TestDynamicLoading.java Mon Mar  9 12:23:59 2015
@@ -267,7 +267,7 @@ public class TestDynamicLoading extends
         10);
   }
 
-  private ByteBuffer getFileContent(String f) throws IOException {
+  public static ByteBuffer getFileContent(String f) throws IOException {
     ByteBuffer jar;
     try (FileInputStream fis = new FileInputStream(getFile(f))) {
       byte[] buf = new byte[fis.available()];