
org.eclipse.jetty.server.AllowedResourceAliasChecker Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of aem-sdk-api Show documentation
Show all versions of aem-sdk-api Show documentation
The Adobe Experience Manager SDK
//
// ========================================================================
// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others.
// ------------------------------------------------------------------------
// 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;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.LinkOption;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import org.eclipse.jetty.server.handler.ContextHandler;
import org.eclipse.jetty.util.component.AbstractLifeCycle;
import org.eclipse.jetty.util.component.LifeCycle;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.util.resource.PathResource;
import org.eclipse.jetty.util.resource.Resource;
/**
* This will approve any alias to anything inside of the {@link ContextHandler}s resource base which
* is not protected by a protected target as defined by {@link ContextHandler#getProtectedTargets()} at start.
* Aliases approved by this may still be able to bypass SecurityConstraints, so this class would need to be extended
* to enforce any additional security constraints that are required.
*
* @deprecated The Eclipse Jetty and Apache Felix Http Jetty packages are no longer supported.
*/
@Deprecated(since = "2021-05-27")
public class AllowedResourceAliasChecker extends AbstractLifeCycle implements ContextHandler.AliasCheck {
private static final Logger LOG = Log.getLogger(AllowedResourceAliasChecker.class);
protected static final LinkOption[] FOLLOW_LINKS = new LinkOption[0];
protected static final LinkOption[] NO_FOLLOW_LINKS = new LinkOption[] { LinkOption.NOFOLLOW_LINKS };
private final ContextHandler _contextHandler;
private final List _protected = new ArrayList<>();
private final AllowedResourceAliasCheckListener _listener = new AllowedResourceAliasCheckListener();
protected Path _base;
/**
* @param contextHandler the context handler to use.
*/
public AllowedResourceAliasChecker(ContextHandler contextHandler) {
_contextHandler = Objects.requireNonNull(contextHandler);
}
protected ContextHandler getContextHandler() {
return _contextHandler;
}
protected void initialize() {
_base = getPath(_contextHandler.getBaseResource());
if (_base == null)
return;
try {
if (Files.exists(_base, NO_FOLLOW_LINKS))
_base = _base.toRealPath(FOLLOW_LINKS);
String[] protectedTargets = _contextHandler.getProtectedTargets();
if (protectedTargets != null) {
for (String s : protectedTargets) _protected.add(_base.getFileSystem().getPath(_base.toString(), s));
}
} catch (IOException e) {
LOG.warn("Base resource failure ({} is disabled): {}", this.getClass().getName(), _base, e);
_base = null;
}
}
@Override
protected void doStart() throws Exception {
// We can only initialize if ContextHandler in started state, the baseResource can be changed even in starting state.
// If the ContextHandler is not started add a listener to delay initialization until fully started.
if (_contextHandler.isStarted())
initialize();
else
_contextHandler.addLifeCycleListener(_listener);
}
@Override
protected void doStop() throws Exception {
_contextHandler.removeLifeCycleListener(_listener);
_base = null;
_protected.clear();
}
@Override
public boolean check(String pathInContext, Resource resource) {
if (_base == null)
return false;
try {
// The existence check resolves the symlinks.
if (!resource.exists())
return false;
Path path = getPath(resource);
if (path == null)
return false;
return check(pathInContext, path);
} catch (Throwable t) {
if (LOG.isDebugEnabled())
LOG.debug("Failed to check alias", t);
return false;
}
}
protected boolean check(String pathInContext, Path path) {
// Allow any aliases (symlinks, 8.3, casing, etc.) so long as
// the resulting real file is allowed.
return isAllowed(getRealPath(path));
}
protected boolean isAllowed(Path path) {
// If the resource doesn't exist we cannot determine whether it is protected so we assume it is.
if (path != null && Files.exists(path)) {
// Walk the path parent links looking for the base resource, but failing if any steps are protected
while (path != null) {
// If the path is the same file as the base, then it is contained in the base and
// is not protected.
if (isSameFile(path, _base))
return true;
// If the path is the same file as any protected resources, then it is protected.
for (Path p : _protected) {
if (isSameFile(path, p))
return false;
}
// Walks up the aliased path name, not the real path name.
// If WEB-INF is a link to /var/lib/webmeta then after checking
// a URI of /WEB-INF/file.xml the parent is /WEB-INF and not /var/lib/webmeta
path = path.getParent();
}
}
return false;
}
protected boolean isSameFile(Path path1, Path path2) {
if (Objects.equals(path1, path2))
return true;
try {
if (Files.isSameFile(path1, path2))
return true;
} catch (Throwable t) {
if (LOG.isDebugEnabled())
LOG.debug("ignored", t);
}
return false;
}
private static Path getRealPath(Path path) {
if (path == null || !Files.exists(path))
return null;
try {
path = path.toRealPath(FOLLOW_LINKS);
if (Files.exists(path))
return path;
} catch (IOException e) {
if (LOG.isDebugEnabled())
LOG.debug("No real path for {}", path, e);
}
return null;
}
protected Path getPath(Resource resource) {
try {
if (resource instanceof PathResource)
return ((PathResource) resource).getPath();
return (resource == null) ? null : resource.getFile().toPath();
} catch (Throwable t) {
LOG.ignore(t);
return null;
}
}
// @deprecated The Eclipse Jetty and Apache Felix Http Jetty packages are no longer supported.
@Deprecated(since = "2021-05-27")
private class AllowedResourceAliasCheckListener implements LifeCycle.Listener {
@Override
public void lifeCycleStarted(LifeCycle event) {
initialize();
}
}
@Override
public String toString() {
String[] protectedTargets = _contextHandler.getProtectedTargets();
return String.format("%s@%x{base=%s,protected=%s}", this.getClass().getSimpleName(), hashCode(), _base, (protectedTargets == null) ? null : Arrays.asList(protectedTargets));
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy