Skip to content

Commit

Permalink
Issue #6497 - AllowedResourceAliasChecker can test target of symlink
Browse files Browse the repository at this point in the history
Signed-off-by: Lachlan Roberts <lachlan@webtide.com>
  • Loading branch information
lachlan-roberts committed Jul 12, 2021
1 parent 32ae130 commit be8c6ac
Showing 1 changed file with 68 additions and 18 deletions.
Expand Up @@ -18,14 +18,18 @@

package org.eclipse.jetty.server;

import java.io.File;

import org.eclipse.jetty.server.handler.ContextHandler;
import org.eclipse.jetty.util.StringUtil;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.util.resource.Resource;

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.LinkOption;
import java.nio.file.Path;

/**
* This will approve an alias to any resource which is not a protected target.
* Except symlinks...
Expand All @@ -34,35 +38,37 @@ public class AllowedResourceAliasChecker implements ContextHandler.AliasCheck
{
private static final Logger LOG = Log.getLogger(AllowedResourceAliasChecker.class);
private final ContextHandler _contextHandler;
private final boolean _checkSymlinkTargets;

public AllowedResourceAliasChecker(ContextHandler contextHandler)
{
this(contextHandler, false);
}

public AllowedResourceAliasChecker(ContextHandler contextHandler, boolean checkSymlinkTargets)
{
_contextHandler = contextHandler;
_checkSymlinkTargets = checkSymlinkTargets;
}

@Override
public boolean check(String uri, Resource resource)
{
try
{
String baseResourcePath = _contextHandler.getBaseResource().getFile().getCanonicalPath();
String resourcePath = resource.getFile().getCanonicalPath();
if (!resourcePath.startsWith(baseResourcePath))
if (!resource.exists())
return false;

for (String s : _contextHandler.getProtectedTargets())
Path resourcePath = resource.getFile().toPath();
Path realPath = resourcePath.toRealPath(LinkOption.NOFOLLOW_LINKS);
if (isProtectedPath(realPath, false))
return false;

if (_checkSymlinkTargets && hasSymbolicLink(resourcePath))
{
String protectedTarget = new File(_contextHandler.getBaseResource().getFile(), s).getCanonicalPath();
if (StringUtil.startsWithIgnoreCase(resourcePath, protectedTarget))
{
if (resourcePath.length() == protectedTarget.length())
return false;

// Check that the target prefix really is a path segment.
char c = resourcePath.charAt(protectedTarget.length());
if (c == File.separatorChar)
return false;
}
realPath = resourcePath.toRealPath();
if (isProtectedPath(realPath, true))
return false;
}
}
catch (Throwable t)
Expand All @@ -71,7 +77,51 @@ public boolean check(String uri, Resource resource)
return false;
}

// TODO: Check symlink targets if flag is set.
return true;
}

private boolean isProtectedPath(Path path, boolean followLinks) throws IOException
{
String basePath = followLinks ? _contextHandler.getBaseResource().getFile().toPath().toRealPath().toString() :
_contextHandler.getBaseResource().getFile().toPath().toRealPath(LinkOption.NOFOLLOW_LINKS).toString();
String targetPath = path.toString();

if (!targetPath.startsWith(basePath))
return true;

for (String s : _contextHandler.getProtectedTargets())
{
String protectedTarget = new File(basePath, s).getCanonicalPath();
if (StringUtil.startsWithIgnoreCase(targetPath, protectedTarget))
{
if (targetPath.length() == protectedTarget.length())
return true;

// Check that the target prefix really is a path segment.
char c = targetPath.charAt(protectedTarget.length());
if (c == File.separatorChar)
return true;
}
}

return false;
}

private boolean hasSymbolicLink(Path path)
{
// Is file itself a symlink?
if (Files.isSymbolicLink(path))
return true;

// Lets try each path segment
Path base = path.getRoot();
for (Path segment : path)
{
base = base.resolve(segment);
if (Files.isSymbolicLink(base))
return true;
}

return false;
}
}

0 comments on commit be8c6ac

Please sign in to comment.