All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.eclipse.jetty.server.AllowedResourceAliasChecker Maven / Gradle / Ivy

There is a newer version: 2025.3.19823.20250304T101418Z-250200
Show newest version
// 
// ========================================================================
// 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