416103 Added AllowSymLinkAliasChecker.java
This commit is contained in:
parent
35d379a335
commit
2b7462e9a9
|
@ -0,0 +1,87 @@
|
||||||
|
//
|
||||||
|
// ========================================================================
|
||||||
|
// Copyright (c) 1995-2013 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.server.handler;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.net.URI;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
|
||||||
|
import org.eclipse.jetty.server.handler.ContextHandler.AliasCheck;
|
||||||
|
import org.eclipse.jetty.util.log.Log;
|
||||||
|
import org.eclipse.jetty.util.log.Logger;
|
||||||
|
import org.eclipse.jetty.util.resource.Resource;
|
||||||
|
|
||||||
|
public class AllowSymLinkAliasChecker implements AliasCheck
|
||||||
|
{
|
||||||
|
private static final Logger LOG = Log.getLogger(AllowSymLinkAliasChecker.class);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean check(String path, Resource resource)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
File file =resource.getFile();
|
||||||
|
if (file==null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// If the file exists
|
||||||
|
if (file.exists())
|
||||||
|
{
|
||||||
|
// we can use the real path method to check the symlinks resolve to the alias
|
||||||
|
URI real = file.toPath().toRealPath().toUri();
|
||||||
|
if (real.equals(resource.getAlias().toURI()))
|
||||||
|
{
|
||||||
|
LOG.debug("Allow symlink {} --> {}",resource,real);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// file does not exists, so we have to walk the path and links ourselves.
|
||||||
|
Path p = file.toPath().toAbsolutePath();
|
||||||
|
File d = p.getRoot().toFile();
|
||||||
|
for (Path e:p)
|
||||||
|
{
|
||||||
|
d=new File(d,e.toString());
|
||||||
|
|
||||||
|
while (d.exists() && Files.isSymbolicLink(d.toPath()))
|
||||||
|
{
|
||||||
|
Path link=Files.readSymbolicLink(d.toPath());
|
||||||
|
if (!link.isAbsolute())
|
||||||
|
link=link.resolve(d.toPath());
|
||||||
|
d=link.toFile().getAbsoluteFile().getCanonicalFile();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (resource.getAlias().toURI().equals(d.toURI()))
|
||||||
|
{
|
||||||
|
LOG.debug("Allow symlink {} --> {}",resource,d);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch(Exception e)
|
||||||
|
{
|
||||||
|
e.printStackTrace();
|
||||||
|
LOG.ignore(e);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -40,6 +40,7 @@ import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.concurrent.CopyOnWriteArrayList;
|
import java.util.concurrent.CopyOnWriteArrayList;
|
||||||
import java.util.concurrent.Future;
|
import java.util.concurrent.Future;
|
||||||
|
|
||||||
import javax.servlet.DispatcherType;
|
import javax.servlet.DispatcherType;
|
||||||
import javax.servlet.Filter;
|
import javax.servlet.Filter;
|
||||||
import javax.servlet.FilterRegistration;
|
import javax.servlet.FilterRegistration;
|
||||||
|
@ -1297,7 +1298,7 @@ public class ContextHandler extends ScopedHandler implements Attributes, Gracefu
|
||||||
LOG.warn(this+" contextPath ends with /*");
|
LOG.warn(this+" contextPath ends with /*");
|
||||||
contextPath=contextPath.substring(0,contextPath.length()-2);
|
contextPath=contextPath.substring(0,contextPath.length()-2);
|
||||||
}
|
}
|
||||||
else if (contextPath.endsWith("/"))
|
else if (contextPath.length()>1 && contextPath.endsWith("/"))
|
||||||
{
|
{
|
||||||
LOG.warn(this+" contextPath ends with /");
|
LOG.warn(this+" contextPath ends with /");
|
||||||
contextPath=contextPath.substring(0,contextPath.length()-1);
|
contextPath=contextPath.substring(0,contextPath.length()-1);
|
||||||
|
@ -2651,5 +2652,5 @@ public class ContextHandler extends ScopedHandler implements Attributes, Gracefu
|
||||||
return r.startsWith(a) && r.length()==a.length()+1 && r.endsWith("/");
|
return r.startsWith(a) && r.length()==a.length()+1 && r.endsWith("/");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -44,7 +44,9 @@ public class ContextHandlerGetResourceTest
|
||||||
private static Server server;
|
private static Server server;
|
||||||
private static ContextHandler context;
|
private static ContextHandler context;
|
||||||
private static File docroot;
|
private static File docroot;
|
||||||
|
private static File otherroot;
|
||||||
private final static AtomicBoolean allowAliases= new AtomicBoolean(false);
|
private final static AtomicBoolean allowAliases= new AtomicBoolean(false);
|
||||||
|
private final static AtomicBoolean allowSymlinks= new AtomicBoolean(false);
|
||||||
|
|
||||||
@BeforeClass
|
@BeforeClass
|
||||||
public static void beforeClass() throws Exception
|
public static void beforeClass() throws Exception
|
||||||
|
@ -60,12 +62,25 @@ public class ContextHandlerGetResourceTest
|
||||||
data.createNewFile();
|
data.createNewFile();
|
||||||
File verylong = new File(sub,"TextFile.Long.txt");
|
File verylong = new File(sub,"TextFile.Long.txt");
|
||||||
verylong.createNewFile();
|
verylong.createNewFile();
|
||||||
|
|
||||||
|
otherroot = new File("target/tests/otherroot").getCanonicalFile().getAbsoluteFile();
|
||||||
|
FS.ensureDirExists(otherroot);
|
||||||
|
FS.ensureEmpty(otherroot);
|
||||||
|
File other = new File(otherroot,"other.txt");
|
||||||
|
other.createNewFile();
|
||||||
|
|
||||||
if (!OS.IS_WINDOWS)
|
File transit = new File(docroot.getParentFile(),"transit");
|
||||||
|
System.err.println("transit "+transit);
|
||||||
|
transit.delete();
|
||||||
|
|
||||||
|
if (OS.IS_UNIX)
|
||||||
{
|
{
|
||||||
// Create alias as 8.3 name so same test will produce an alias on both windows an normal systems
|
// Create alias as 8.3 name so same test will produce an alias on both windows an normal systems
|
||||||
File eightDotThree=new File(sub,"TEXTFI~1.TXT");
|
File eightDotThree=new File(sub,"TEXTFI~1.TXT");
|
||||||
Files.createSymbolicLink(eightDotThree.toPath(),verylong.toPath());
|
Files.createSymbolicLink(eightDotThree.toPath(),verylong.toPath());
|
||||||
|
|
||||||
|
Files.createSymbolicLink(new File(docroot,"other").toPath(),new File("../transit").toPath());
|
||||||
|
Files.createSymbolicLink(transit.toPath(),otherroot.toPath());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -74,13 +89,18 @@ public class ContextHandlerGetResourceTest
|
||||||
context.setBaseResource(Resource.newResource(docroot));
|
context.setBaseResource(Resource.newResource(docroot));
|
||||||
context.addAliasCheck(new ContextHandler.AliasCheck()
|
context.addAliasCheck(new ContextHandler.AliasCheck()
|
||||||
{
|
{
|
||||||
|
final AllowSymLinkAliasChecker symlinkcheck = new AllowSymLinkAliasChecker();
|
||||||
@Override
|
@Override
|
||||||
public boolean check(String path, Resource resource)
|
public boolean check(String path, Resource resource)
|
||||||
{
|
{
|
||||||
|
if (allowAliases.get())
|
||||||
|
return true;
|
||||||
|
if (allowSymlinks.get())
|
||||||
|
return symlinkcheck.check(path,resource);
|
||||||
return allowAliases.get();
|
return allowAliases.get();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
server.setHandler(context);
|
server.setHandler(context);
|
||||||
server.start();
|
server.start();
|
||||||
}
|
}
|
||||||
|
@ -308,4 +328,57 @@ public class ContextHandlerGetResourceTest
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSymlinkKnown() throws Exception
|
||||||
|
{
|
||||||
|
if (!OS.IS_UNIX)
|
||||||
|
return;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
allowSymlinks.set(true);
|
||||||
|
|
||||||
|
final String path="/other/other.txt";
|
||||||
|
|
||||||
|
Resource resource=context.getResource(path);
|
||||||
|
assertNotNull(resource);
|
||||||
|
assertEquals("other.txt",resource.getFile().getName());
|
||||||
|
assertEquals(docroot,resource.getFile().getParentFile().getParentFile());
|
||||||
|
assertTrue(resource.exists());
|
||||||
|
|
||||||
|
URL url=context.getServletContext().getResource(path);
|
||||||
|
assertEquals(docroot,new File(url.toURI()).getParentFile().getParentFile());
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
allowSymlinks.set(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSymlinkUnknown() throws Exception
|
||||||
|
{
|
||||||
|
if (!OS.IS_UNIX)
|
||||||
|
return;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
allowSymlinks.set(true);
|
||||||
|
|
||||||
|
final String path="/other/unknown.txt";
|
||||||
|
|
||||||
|
Resource resource=context.getResource(path);
|
||||||
|
assertNotNull(resource);
|
||||||
|
assertEquals("unknown.txt",resource.getFile().getName());
|
||||||
|
assertEquals(docroot,resource.getFile().getParentFile().getParentFile());
|
||||||
|
assertFalse(resource.exists());
|
||||||
|
|
||||||
|
URL url=context.getServletContext().getResource(path);
|
||||||
|
assertNull(url);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
allowSymlinks.set(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue