This is an automated email from the ASF dual-hosted git repository.
janhoy pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/solr.git
The following commit(s) were added to refs/heads/main by this push:
new c1f916d SOLR-15826: ResourceLoader should better respect allowed
paths (#439)
c1f916d is described below
commit c1f916d532e9580f22c09b6ad746d4ea9bcc47a6
Author: Jan Høydahl <[email protected]>
AuthorDate: Fri Dec 3 23:39:25 2021 +0100
SOLR-15826: ResourceLoader should better respect allowed paths (#439)
---
solr/CHANGES.txt | 2 +
.../org/apache/solr/core/SolrResourceLoader.java | 58 ++++++++++++----------
.../solr/schema/ManagedIndexSchemaFactory.java | 2 +-
.../org/apache/solr/core/ResourceLoaderTest.java | 41 +++++++++++++--
.../apache/solr/schema/PrimitiveFieldTypeTest.java | 5 +-
.../org/apache/solr/util/TestSystemIdResolver.java | 4 +-
6 files changed, 76 insertions(+), 36 deletions(-)
diff --git a/solr/CHANGES.txt b/solr/CHANGES.txt
index c6c68d8..9b04022 100644
--- a/solr/CHANGES.txt
+++ b/solr/CHANGES.txt
@@ -461,6 +461,8 @@ Bug Fixes
* SOLR-15825: Security UI 'hasPermission' check should check if the user has
the "all" permission if the requested permission is not defined
to match how the backend works (Timothy Potter)
+* SOLR-15826: ResourceLoader should better respect allowed paths (janhoy)
+
* SOLR-15828: AuthTool (in SolrCLI) should include the config-read,
collection-admin-read, core-admin-read, and all permissions in the initial
security.json
to avoid warnings in the security UI (Timothy Potter)
diff --git a/solr/core/src/java/org/apache/solr/core/SolrResourceLoader.java
b/solr/core/src/java/org/apache/solr/core/SolrResourceLoader.java
index 83e15a2..bac4709 100644
--- a/solr/core/src/java/org/apache/solr/core/SolrResourceLoader.java
+++ b/solr/core/src/java/org/apache/solr/core/SolrResourceLoader.java
@@ -90,6 +90,8 @@ public class SolrResourceLoader implements ResourceLoader,
Closeable, SolrClassL
"spelling.suggest.", "spelling.suggest.fst.", "rest.schema.analysis.",
"security.", "handler.admin."
};
private static final Charset UTF_8 = StandardCharsets.UTF_8;
+ public static final String SOLR_ALLOW_UNSAFE_RESOURCELOADING_PARAM =
"solr.allow.unsafe.resourceloading";
+ private boolean allowUnsafeResourceloading;
private String name = "";
@@ -164,7 +166,8 @@ public class SolrResourceLoader implements ResourceLoader,
Closeable, SolrClassL
* found in the "lib/" directory in the specified instance directory.
*/
public SolrResourceLoader(Path instanceDir, ClassLoader parent) {
- if (instanceDir == null) {
+ allowUnsafeResourceloading =
Boolean.getBoolean(SOLR_ALLOW_UNSAFE_RESOURCELOADING_PARAM);
+ if (instanceDir == null) {
throw new NullPointerException("SolrResourceLoader instanceDir must be
non-null");
}
@@ -320,16 +323,6 @@ public class SolrResourceLoader implements ResourceLoader,
Closeable, SolrClassL
return classLoader;
}
- private Path checkPathIsSafe(Path pathToCheck) throws IOException {
- if (Boolean.getBoolean("solr.allow.unsafe.resourceloading"))
- return pathToCheck;
- pathToCheck = pathToCheck.normalize();
- if (pathToCheck.startsWith(instanceDir))
- return pathToCheck;
- throw new IOException("File " + pathToCheck + " is outside resource loader
dir " + instanceDir +
- "; set -Dsolr.allow.unsafe.resourceloading=true to allow unsafe
loading");
- }
-
/**
* Opens any resource by its name.
* By default, this will look in multiple locations to load the resource:
@@ -342,15 +335,21 @@ public class SolrResourceLoader implements
ResourceLoader, Closeable, SolrClassL
*/
@Override
public InputStream openResource(String resource) throws IOException {
+ if (resource.trim().startsWith("\\\\")) { // Always disallow UNC paths
+ throw new SolrResourceNotFoundException("Resource '" + resource + "'
could not be loaded.");
+ }
+ Path instanceDir = getInstancePath().normalize();
+ Path inInstanceDir = getInstancePath().resolve(resource).normalize();
+ Path inConfigDir =
instanceDir.resolve("conf").resolve(resource).normalize();
+ if (inInstanceDir.startsWith(instanceDir) || allowUnsafeResourceloading) {
+ // The resource is either inside instance dir or we allow unsafe
loading, so allow testing if file exists
+ if (Files.exists(inConfigDir) && Files.isReadable(inConfigDir)) {
+ return Files.newInputStream(inConfigDir);
+ }
- Path inConfigDir = getInstancePath().resolve("conf").resolve(resource);
- if (Files.exists(inConfigDir) && Files.isReadable(inConfigDir)) {
- return Files.newInputStream(checkPathIsSafe(inConfigDir));
- }
-
- Path inInstanceDir = getInstancePath().resolve(resource);
- if (Files.exists(inInstanceDir) && Files.isReadable(inInstanceDir)) {
- return Files.newInputStream(checkPathIsSafe(inInstanceDir));
+ if (Files.exists(inInstanceDir) && Files.isReadable(inInstanceDir)) {
+ return Files.newInputStream(inInstanceDir);
+ }
}
// Delegate to the class loader (looking into $INSTANCE_DIR/lib jars).
@@ -373,13 +372,20 @@ public class SolrResourceLoader implements
ResourceLoader, Closeable, SolrClassL
* Report the location of a resource found by the resource loader
*/
public String resourceLocation(String resource) {
- Path inConfigDir = getInstancePath().resolve("conf").resolve(resource);
- if (Files.exists(inConfigDir) && Files.isReadable(inConfigDir))
- return inConfigDir.normalize().toString();
+ if (resource.trim().startsWith("\\\\")) {
+ // Disallow UNC
+ return null;
+ }
+ Path inInstanceDir = instanceDir.resolve(resource).normalize();
+ Path inConfigDir =
instanceDir.resolve("conf").resolve(resource).normalize();
+ boolean isRelativeToInstanceDir =
inInstanceDir.startsWith(instanceDir.normalize());
+ if (isRelativeToInstanceDir || allowUnsafeResourceloading) {
+ if (Files.exists(inConfigDir) && Files.isReadable(inConfigDir))
+ return inConfigDir.normalize().toString();
- Path inInstanceDir = getInstancePath().resolve(resource);
- if (Files.exists(inInstanceDir) && Files.isReadable(inInstanceDir))
- return inInstanceDir.normalize().toString();
+ if (Files.exists(inInstanceDir) && Files.isReadable(inInstanceDir))
+ return inInstanceDir.normalize().toString();
+ }
try (InputStream is =
classLoader.getResourceAsStream(resource.replace(File.separatorChar, '/'))) {
if (is != null)
@@ -388,7 +394,7 @@ public class SolrResourceLoader implements ResourceLoader,
Closeable, SolrClassL
// ignore
}
- return resource;
+ return allowUnsafeResourceloading ? resource : null;
}
/**
diff --git
a/solr/core/src/java/org/apache/solr/schema/ManagedIndexSchemaFactory.java
b/solr/core/src/java/org/apache/solr/schema/ManagedIndexSchemaFactory.java
index 7c3e670..f027486 100644
--- a/solr/core/src/java/org/apache/solr/schema/ManagedIndexSchemaFactory.java
+++ b/solr/core/src/java/org/apache/solr/schema/ManagedIndexSchemaFactory.java
@@ -383,7 +383,7 @@ public class ManagedIndexSchemaFactory extends
IndexSchemaFactory implements Sol
*/
private File locateConfigFile(String resource) {
String location = config.getResourceLoader().resourceLocation(resource);
- if (location.equals(resource) || location.startsWith("classpath:"))
+ if (location == null || location.equals(resource) ||
location.startsWith("classpath:"))
return null;
return new File(location);
}
diff --git a/solr/core/src/test/org/apache/solr/core/ResourceLoaderTest.java
b/solr/core/src/test/org/apache/solr/core/ResourceLoaderTest.java
index de9bd39..d37f749 100644
--- a/solr/core/src/test/org/apache/solr/core/ResourceLoaderTest.java
+++ b/solr/core/src/test/org/apache/solr/core/ResourceLoaderTest.java
@@ -42,12 +42,18 @@ import org.apache.solr.handler.admin.LukeRequestHandler;
import org.apache.solr.handler.component.FacetComponent;
import org.apache.solr.response.JSONResponseWriter;
import org.apache.solr.util.plugin.SolrCoreAware;
+import org.junit.After;
-import static org.apache.solr.core.SolrResourceLoader.assertAwareCompatibility;
-import static org.apache.solr.core.SolrResourceLoader.clearCache;
+import static org.apache.solr.core.SolrResourceLoader.*;
import static org.hamcrest.core.Is.is;
public class ResourceLoaderTest extends SolrTestCaseJ4 {
+ @Override
+ @After
+ public void tearDown() throws Exception {
+ super.tearDown();
+ setUnsafeResourceLoading(false);
+ }
public void testInstanceDir() throws Exception {
final Path dir = createTempDir();
@@ -63,13 +69,38 @@ public class ResourceLoaderTest extends SolrTestCaseJ4 {
Path instanceDir = temp.resolve("instance");
Files.createDirectories(instanceDir.resolve("conf"));
+ setUnsafeResourceLoading(false);
+ try (SolrResourceLoader loader = new SolrResourceLoader(instanceDir)) {
+ // Path traversal
+ assertTrue(assertThrows(IOException.class, () ->
+
loader.openResource("../../dummy.txt").close()).getMessage().contains("Can't
find resource"));
+ assertNull(loader.resourceLocation("../../dummy.txt"));
+
+ // UNC paths
+ assertTrue(assertThrows(SolrResourceNotFoundException.class, () ->
+
loader.openResource("\\\\192.168.10.10\\foo").close()).getMessage().contains("Resource
'\\\\192.168.10.10\\foo' could not be loaded."));
+ assertNull(loader.resourceLocation("\\\\192.168.10.10\\foo"));
+ }
+
+ setUnsafeResourceLoading(true);
try (SolrResourceLoader loader = new SolrResourceLoader(instanceDir)) {
+ // Path traversal - unsafe but allowed
loader.openResource("../../dummy.txt").close();
- fail();
- } catch (IOException ioe) {
- assertTrue(ioe.getMessage().contains("is outside resource loader dir"));
+ assertNotNull(loader.resourceLocation("../../dummy.txt"));
+
+ // UNC paths never allowed
+ assertTrue(assertThrows(SolrResourceNotFoundException.class, () ->
+
loader.openResource("\\\\192.168.10.10\\foo").close()).getMessage().contains("Resource
'\\\\192.168.10.10\\foo' could not be loaded."));
+ assertNull(loader.resourceLocation("\\\\192.168.10.10\\foo"));
}
+ }
+ private void setUnsafeResourceLoading(boolean unsafe) {
+ if (unsafe) {
+ System.setProperty(SOLR_ALLOW_UNSAFE_RESOURCELOADING_PARAM, "true");
+ } else {
+ System.clearProperty(SOLR_ALLOW_UNSAFE_RESOURCELOADING_PARAM);
+ }
}
@SuppressWarnings({"unchecked"})
diff --git
a/solr/core/src/test/org/apache/solr/schema/PrimitiveFieldTypeTest.java
b/solr/core/src/test/org/apache/solr/schema/PrimitiveFieldTypeTest.java
index 4b9b2dc..3dd53c6 100644
--- a/solr/core/src/test/org/apache/solr/schema/PrimitiveFieldTypeTest.java
+++ b/solr/core/src/test/org/apache/solr/schema/PrimitiveFieldTypeTest.java
@@ -24,6 +24,7 @@ import java.util.Map;
import org.apache.solr.SolrTestCaseJ4;
import org.apache.solr.core.SolrConfig;
+import org.apache.solr.core.SolrResourceLoader;
import org.junit.Test;
/**
@@ -42,7 +43,7 @@ public class PrimitiveFieldTypeTest extends SolrTestCaseJ4 {
System.setProperty("enable.update.log", "false"); // schema12 doesn't
support _version_
System.setProperty("solr.test.sys.prop1", "propone");
System.setProperty("solr.test.sys.prop2", "proptwo");
- System.setProperty("solr.allow.unsafe.resourceloading", "true");
+
System.setProperty(SolrResourceLoader.SOLR_ALLOW_UNSAFE_RESOURCELOADING_PARAM,
"true");
initMap = new HashMap<>();
config = new SolrConfig(TEST_PATH().resolve("collection1"), testConfHome +
"solrconfig.xml");
@@ -50,7 +51,7 @@ public class PrimitiveFieldTypeTest extends SolrTestCaseJ4 {
@Override
public void tearDown() throws Exception {
- System.clearProperty("solr.allow.unsafe.resourceloading");
+
System.clearProperty(SolrResourceLoader.SOLR_ALLOW_UNSAFE_RESOURCELOADING_PARAM);
super.tearDown();
}
diff --git a/solr/core/src/test/org/apache/solr/util/TestSystemIdResolver.java
b/solr/core/src/test/org/apache/solr/util/TestSystemIdResolver.java
index 18a7f70..a64f169 100644
--- a/solr/core/src/test/org/apache/solr/util/TestSystemIdResolver.java
+++ b/solr/core/src/test/org/apache/solr/util/TestSystemIdResolver.java
@@ -30,7 +30,7 @@ import org.xml.sax.InputSource;
public class TestSystemIdResolver extends SolrTestCaseJ4 {
public void tearDown() throws Exception {
- System.clearProperty("solr.allow.unsafe.resourceloading");
+
System.clearProperty(SolrResourceLoader.SOLR_ALLOW_UNSAFE_RESOURCELOADING_PARAM);
super.tearDown();
}
@@ -93,7 +93,7 @@ public class TestSystemIdResolver extends SolrTestCaseJ4 {
}
public void testUnsafeResolving() throws Exception {
- System.setProperty("solr.allow.unsafe.resourceloading", "true");
+
System.setProperty(SolrResourceLoader.SOLR_ALLOW_UNSAFE_RESOURCELOADING_PARAM,
"true");
final Path testHome =
SolrTestCaseJ4.getFile("solr/collection1").getParentFile().toPath();
final ResourceLoader loader = new
SolrResourceLoader(testHome.resolve("collection1"),
this.getClass().getClassLoader());