From 9009f6abbf5ca7d4c34da08820de5ea377b56138 Mon Sep 17 00:00:00 2001 From: Joakim Erdfelt Date: Thu, 27 Dec 2012 11:44:19 -0700 Subject: [PATCH] Adding safety checks for FileResource references with null characters. * Adding testcase for Jsp + DefaultServlet w/aliasing turned on with null characters in the request url. * Adding validateUri() method to FileResource --- .../jetty/util/resource/FileResource.java | 48 +- .../jetty/util/resource/FileResourceTest.java | 97 ++++ test-jetty-webapp/pom.xml | 11 + .../org/eclipse/jetty/JspMatchingTest.java | 172 ++++++ .../src/test/resources/realm.properties | 21 + .../resources/webdefault-with-aliases.xml | 527 ++++++++++++++++++ 6 files changed, 875 insertions(+), 1 deletion(-) create mode 100644 jetty-util/src/test/java/org/eclipse/jetty/util/resource/FileResourceTest.java create mode 100644 test-jetty-webapp/src/test/java/org/eclipse/jetty/JspMatchingTest.java create mode 100644 test-jetty-webapp/src/test/resources/realm.properties create mode 100644 test-jetty-webapp/src/test/resources/webdefault-with-aliases.xml diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/resource/FileResource.java b/jetty-util/src/main/java/org/eclipse/jetty/util/resource/FileResource.java index 211de5ba5fc..5a2e4e83782 100644 --- a/jetty-util/src/main/java/org/eclipse/jetty/util/resource/FileResource.java +++ b/jetty-util/src/main/java/org/eclipse/jetty/util/resource/FileResource.java @@ -75,6 +75,37 @@ public class FileResource extends URLResource return __checkAliases; } + /** + * Perform some basic validation of the characters in the string for invalid + * codepoints and null characters. + * + * @param str the string to validate + * @throws URISyntaxException thrown if invalid characters are encountered + */ + private static final String validateUri(String str) throws URISyntaxException + { + if (str == null) + { + return str; + } + + int len = str.length(); + int codepoint; + for (int i = 0; i < len; i++) + { + codepoint = str.codePointAt(i); + if (codepoint == 0) + { + throw new URISyntaxException(str,"Encountered NULL character"); + } + if (Character.isISOControl(codepoint)) + { + throw new URISyntaxException(str,"Encountered ISO Control Code"); + } + } + return str; + } + /* -------------------------------------------------------- */ public FileResource(URL url) throws IOException, URISyntaxException @@ -84,8 +115,23 @@ public class FileResource extends URLResource try { // Try standard API to convert URL to file. + + /* Note: + * If the passed in URL has a null at the end of the string, then + * url.toExternalForm() and url.toString() strip that knowledge out. + * Which can lead to false positives for .exists() calls. + * + * The URL should be validated in parts, then passed to the File object. + */ + validateUri(url.getFile()); + validateUri(url.getPath()); + _file =new File(new URI(url.toString())); } + catch (URISyntaxException e) + { + throw e; + } catch (Exception e) { LOG.ignore(e); @@ -98,7 +144,7 @@ public class FileResource extends URLResource if (uri.getAuthority()==null) _file = new File(uri); else - _file = new File("//"+uri.getAuthority()+URIUtil.decodePath(url.getFile())); + _file = new File(validateUri("//"+uri.getAuthority()+URIUtil.decodePath(url.getFile()))); } catch (Exception e2) { diff --git a/jetty-util/src/test/java/org/eclipse/jetty/util/resource/FileResourceTest.java b/jetty-util/src/test/java/org/eclipse/jetty/util/resource/FileResourceTest.java new file mode 100644 index 00000000000..e2669ddee79 --- /dev/null +++ b/jetty-util/src/test/java/org/eclipse/jetty/util/resource/FileResourceTest.java @@ -0,0 +1,97 @@ +// +// ======================================================================== +// Copyright (c) 1995-2012 Mort Bay Consulting Pty. Ltd. +// ------------------------------------------------------------------------ +// All rights reserved. This program and the accompanying materials +// are made available under the terms of the Eclipse Public License v1.0 +// and Apache License v2.0 which accompanies this distribution. +// +// The Eclipse Public License is available at +// http://www.eclipse.org/legal/epl-v10.html +// +// The Apache License v2.0 is available at +// http://www.opensource.org/licenses/apache2.0.php +// +// You may elect to redistribute this code under either of these licenses. +// ======================================================================== +// + +package org.eclipse.jetty.util.resource; + +import static org.hamcrest.Matchers.*; + +import java.io.File; +import java.io.IOException; +import java.net.MalformedURLException; +import java.net.URI; +import java.net.URISyntaxException; +import java.net.URL; + +import org.eclipse.jetty.toolchain.test.TestingDir; +import org.eclipse.jetty.util.StringUtil; +import org.eclipse.jetty.util.UrlEncoded; +import org.junit.Assert; +import org.junit.Ignore; +import org.junit.Rule; +import org.junit.Test; + +public class FileResourceTest +{ + @Rule + public TestingDir testdir = new TestingDir(); + + private URI createDummyFile(String name) throws IOException + { + File file = testdir.getFile(name); + file.createNewFile(); + return file.toURI(); + } + + private URL decode(URL url) throws MalformedURLException + { + String raw = url.toExternalForm(); + String decoded = UrlEncoded.decodeString(raw,0,raw.length(),StringUtil.__UTF8); + return new URL(decoded); + } + + @Test + public void testExist_Normal() throws Exception + { + createDummyFile("a.jsp"); + + URI ref = testdir.getDir().toURI().resolve("a.jsp"); + FileResource fileres = new FileResource(decode(ref.toURL())); + Assert.assertThat("FileResource: " + fileres,fileres.exists(),is(true)); + } + + @Ignore("Cannot get null to be seen by FileResource") + @Test + public void testExist_BadNull() throws Exception + { + createDummyFile("a.jsp"); + + try { + // request with null at end + URI ref = testdir.getDir().toURI().resolve("a.jsp%00"); + FileResource fileres = new FileResource(decode(ref.toURL())); + Assert.assertThat("FileResource: " + fileres,fileres.exists(),is(false)); + } catch(URISyntaxException e) { + // Valid path + } + } + + @Test + public void testExist_BadNullX() throws Exception + { + createDummyFile("a.jsp"); + + try { + // request with null and x at end + URI ref = testdir.getDir().toURI().resolve("a.jsp%00x"); + FileResource fileres = new FileResource(decode(ref.toURL())); + Assert.assertThat("FileResource: " + fileres,fileres.exists(),is(false)); + } catch(URISyntaxException e) { + // Valid path + } + } +} diff --git a/test-jetty-webapp/pom.xml b/test-jetty-webapp/pom.xml index c5a40211174..7bf5369ebf4 100644 --- a/test-jetty-webapp/pom.xml +++ b/test-jetty-webapp/pom.xml @@ -214,5 +214,16 @@ 1.2 provided + + org.eclipse.jetty.toolchain + jetty-test-helper + test + + + org.eclipse.jetty + jetty-jsp + ${project.version} + test + diff --git a/test-jetty-webapp/src/test/java/org/eclipse/jetty/JspMatchingTest.java b/test-jetty-webapp/src/test/java/org/eclipse/jetty/JspMatchingTest.java new file mode 100644 index 00000000000..baf045ec4bc --- /dev/null +++ b/test-jetty-webapp/src/test/java/org/eclipse/jetty/JspMatchingTest.java @@ -0,0 +1,172 @@ +// +// ======================================================================== +// Copyright (c) 1995-2012 Mort Bay Consulting Pty. Ltd. +// ------------------------------------------------------------------------ +// All rights reserved. This program and the accompanying materials +// are made available under the terms of the Eclipse Public License v1.0 +// and Apache License v2.0 which accompanies this distribution. +// +// The Eclipse Public License is available at +// http://www.eclipse.org/legal/epl-v10.html +// +// The Apache License v2.0 is available at +// http://www.opensource.org/licenses/apache2.0.php +// +// You may elect to redistribute this code under either of these licenses. +// ======================================================================== +// + +package org.eclipse.jetty; + +import static org.hamcrest.Matchers.*; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.net.HttpURLConnection; +import java.net.URI; + +import org.eclipse.jetty.security.HashLoginService; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.server.handler.ContextHandlerCollection; +import org.eclipse.jetty.server.handler.HandlerList; +import org.eclipse.jetty.server.nio.SelectChannelConnector; +import org.eclipse.jetty.toolchain.test.MavenTestingUtils; +import org.eclipse.jetty.util.IO; +import org.eclipse.jetty.util.resource.Resource; +import org.eclipse.jetty.webapp.WebAppContext; +import org.junit.AfterClass; +import org.junit.Assert; +import org.junit.BeforeClass; +import org.junit.Test; + +public class JspMatchingTest +{ + private static Server server; + private static URI serverURI; + + @BeforeClass + public static void startServer() throws Exception + { + server = new Server(); + SelectChannelConnector connector = new SelectChannelConnector(); + connector.setPort(0); + server.addConnector(connector); + + // Configure LoginService + HashLoginService login = new HashLoginService(); + login.setName("Test Realm"); + File realmFile = MavenTestingUtils.getTestResourceFile("realm.properties"); + login.setConfig(realmFile.getAbsolutePath()); + server.addBean(login); + + // Configure WebApp + HandlerList handlers = new HandlerList(); + ContextHandlerCollection contexts = new ContextHandlerCollection(); + WebAppContext context = new WebAppContext(); + + File webappBase = MavenTestingUtils.getProjectDir("src/main/webapp"); + Resource resBase = Resource.newResource(webappBase); + context.setBaseResource(resBase); + + File aliasedWebDefault = MavenTestingUtils.getTestResourceFile("webdefault-with-aliases.xml"); + context.setDefaultsDescriptor(aliasedWebDefault.getAbsolutePath()); + context.setContextPath("/"); + server.setHandler(handlers); + handlers.addHandler(contexts); + contexts.addHandler(context); + + server.start(); + + serverURI = new URI("http://localhost:" + connector.getLocalPort() + "/"); + + } + + @AfterClass + public static void stopServer() throws Exception + { + server.stop(); + } + + @Test + public void testGetBeanRef() throws Exception + { + + URI uri = serverURI.resolve("/jsp/bean1.jsp"); + + HttpURLConnection conn = null; + try + { + conn = (HttpURLConnection)uri.toURL().openConnection(); + Assert.assertThat(conn.getResponseCode(),is(200)); + System.err.printf("Response Code: %d%n", conn.getResponseCode()); + + // make sure that jsp actually ran, and didn't just get passed onto + // the default servlet to return the jsp source + String body = getResponseBody(conn); + Assert.assertThat("Body", body, not(containsString("<%@"))); + Assert.assertThat("Body", body, not(containsString(": [, ...] +# +# Passwords may be clear text, obfuscated or checksummed. The class +# org.eclipse.util.Password should be used to generate obfuscated +# passwords or password checksums +# +# If DIGEST Authentication is used, the password must be in a recoverable +# format, either plain text or OBF:. +# +jetty: MD5:164c88b302622e17050af52c89945d44,user +admin: CRYPT:adpexzg3FUZAk,server-administrator,content-administrator,admin +other: OBF:1xmk1w261u9r1w1c1xmq,user +plain: plain,user +user: password,user + +# This entry is for digest auth. The credential is a MD5 hash of username:realmname:password +digest: MD5:6e120743ad67abfbc385bc2bb754e297,user diff --git a/test-jetty-webapp/src/test/resources/webdefault-with-aliases.xml b/test-jetty-webapp/src/test/resources/webdefault-with-aliases.xml new file mode 100644 index 00000000000..db14fd9637b --- /dev/null +++ b/test-jetty-webapp/src/test/resources/webdefault-with-aliases.xml @@ -0,0 +1,527 @@ + + + + + + + + + + + + + + + + + + + + + + + Default web.xml file. + This file is applied to a Web application before it's own WEB_INF/web.xml file + + + + + + + + org.eclipse.jetty.servlet.listener.ELContextCleaner + + + + + + + + org.eclipse.jetty.servlet.listener.IntrospectorCleaner + + + + + + + + + + + + + + + + + + + default + org.eclipse.jetty.servlet.DefaultServlet + + aliases + true + + + acceptRanges + true + + + dirAllowed + true + + + welcomeServlets + false + + + redirectWelcome + false + + + maxCacheSize + 256000000 + + + maxCachedFileSize + 200000000 + + + maxCachedFiles + 2048 + + + gzip + true + + + useFileMappedBuffer + true + + + + 0 + + + + default + / + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + jsp + org.apache.jasper.servlet.JspServlet + + logVerbosityLevel + DEBUG + + + fork + false + + + xpoweredBy + false + + + 0 + + + + jsp + *.jsp + *.jspf + *.jspx + *.xsp + *.JSP + *.JSPF + *.JSPX + *.XSP + + + + + + + + + + + + + + + + + + + + + + + + + + + + 30 + + + + + + + + + + + + + index.html + index.htm + index.jsp + + + + + + ar + ISO-8859-6 + + + be + ISO-8859-5 + + + bg + ISO-8859-5 + + + ca + ISO-8859-1 + + + cs + ISO-8859-2 + + + da + ISO-8859-1 + + + de + ISO-8859-1 + + + el + ISO-8859-7 + + + en + ISO-8859-1 + + + es + ISO-8859-1 + + + et + ISO-8859-1 + + + fi + ISO-8859-1 + + + fr + ISO-8859-1 + + + hr + ISO-8859-2 + + + hu + ISO-8859-2 + + + is + ISO-8859-1 + + + it + ISO-8859-1 + + + iw + ISO-8859-8 + + + ja + Shift_JIS + + + ko + EUC-KR + + + lt + ISO-8859-2 + + + lv + ISO-8859-2 + + + mk + ISO-8859-5 + + + nl + ISO-8859-1 + + + no + ISO-8859-1 + + + pl + ISO-8859-2 + + + pt + ISO-8859-1 + + + ro + ISO-8859-2 + + + ru + ISO-8859-5 + + + sh + ISO-8859-5 + + + sk + ISO-8859-2 + + + sl + ISO-8859-2 + + + sq + ISO-8859-2 + + + sr + ISO-8859-5 + + + sv + ISO-8859-1 + + + tr + ISO-8859-9 + + + uk + ISO-8859-5 + + + zh + GB2312 + + + zh_TW + Big5 + + + + + + Disable TRACE + / + TRACE + + + + + +