lgoldstein commented on code in PR #362:
URL: https://github.com/apache/mina-sshd/pull/362#discussion_r1172862559


##########
sshd-common/src/main/java/org/apache/sshd/common/file/root/RootedDirectoryStream.java:
##########
@@ -0,0 +1,63 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sshd.common.file.root;
+
+import java.io.IOException;
+import java.nio.file.DirectoryStream;
+import java.nio.file.Path;
+import java.util.Iterator;
+
+/**
+ * secure directory stream proxy for a {@link RootedFileSystem}
+ *
+ * @author <a href="mailto:dev@mina.apache.org";>Apache MINA SSHD Project</a>
+ */
+class RootedDirectoryStream implements DirectoryStream<Path> {

Review Comment:
   I think this class (and its constructor) should be *public* since we want to 
give our users the flexibility to implement behavior override if they should 
feel like it.



##########
sshd-common/src/main/java/org/apache/sshd/common/file/root/RootedFileSystemUtils.java:
##########
@@ -0,0 +1,49 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sshd.common.file.root;
+
+import java.nio.file.InvalidPathException;
+import java.nio.file.Path;
+
+/**
+ * Utility functions for rooted file utils
+ */
+public final class RootedFileSystemUtils {
+
+    private RootedFileSystemUtils() {
+        // do not construct
+    }
+
+    static void validateSafeRelativeSymlink(Path target) {

Review Comment:
   I believe the method should also be *public*



##########
sshd-common/src/main/java/org/apache/sshd/common/file/root/RootedSecureDirectoryStream.java:
##########
@@ -0,0 +1,92 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sshd.common.file.root;
+
+import java.io.IOException;
+import java.nio.channels.SeekableByteChannel;
+import java.nio.file.LinkOption;
+import java.nio.file.OpenOption;
+import java.nio.file.Path;
+import java.nio.file.SecureDirectoryStream;
+import java.nio.file.attribute.FileAttribute;
+import java.nio.file.attribute.FileAttributeView;
+import java.util.Set;
+
+/**
+ * A secure directory stream proxy for a {@link RootedFileSystem}
+ *
+ * @author <a href="mailto:dev@mina.apache.org";>Apache MINA SSHD Project</a>
+ */
+class RootedSecureDirectoryStream extends RootedDirectoryStream implements 
SecureDirectoryStream<Path> {
+
+    RootedSecureDirectoryStream(RootedFileSystem rfs, 
SecureDirectoryStream<Path> delegate) {
+        super(rfs, delegate);
+    }
+
+    @Override
+    public SecureDirectoryStream<Path> newDirectoryStream(Path path, 
LinkOption... options) throws IOException {
+        return new RootedSecureDirectoryStream(rfs, 
delegate().newDirectoryStream(fixPath(path), options));
+    }
+
+    private Path fixPath(Path p) {

Review Comment:
   Should be *protected* in order to allow users to override the behavior if 
they should wish to do so.



##########
sshd-common/src/main/java/org/apache/sshd/common/file/root/RootedSecureDirectoryStream.java:
##########
@@ -0,0 +1,92 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sshd.common.file.root;
+
+import java.io.IOException;
+import java.nio.channels.SeekableByteChannel;
+import java.nio.file.LinkOption;
+import java.nio.file.OpenOption;
+import java.nio.file.Path;
+import java.nio.file.SecureDirectoryStream;
+import java.nio.file.attribute.FileAttribute;
+import java.nio.file.attribute.FileAttributeView;
+import java.util.Set;
+
+/**
+ * A secure directory stream proxy for a {@link RootedFileSystem}
+ *
+ * @author <a href="mailto:dev@mina.apache.org";>Apache MINA SSHD Project</a>
+ */
+class RootedSecureDirectoryStream extends RootedDirectoryStream implements 
SecureDirectoryStream<Path> {

Review Comment:
   See  previous comments regarding *public* visibility...



##########
sshd-common/src/main/java/org/apache/sshd/common/file/util/BaseFileSystem.java:
##########
@@ -121,6 +121,23 @@ protected void appendDedupSep(StringBuilder sb, 
CharSequence s) {
         }
     }
 
+    /**
+     * In case we are running on Windows, accept "\\" as a file separator. 
Ignore in *nix as "\\" is a valid filename
+     * 
+     * @param  name the name to fix the separator for if running on Windows
+     * @return      the fixed name
+     */
+    private String handleWindowsSeparator(String name) {

Review Comment:
   Should be *protected* in order to allow users to override the behavior if 
they should wish to do so.



##########
sshd-common/src/main/java/org/apache/sshd/common/util/io/IoUtils.java:
##########
@@ -415,6 +442,19 @@ public static Boolean checkFileExists(Path path, 
LinkOption... options) {
         }
     }
 
+    private static Path getPartialPath(Path path, int partsToExtract) {

Review Comment:
   Should be *public* in case users want/need to use it



##########
sshd-common/src/main/java/org/apache/sshd/common/util/io/IoUtils.java:
##########
@@ -637,4 +677,75 @@ public static List<String> readAllLines(BufferedReader 
reader, int lineCountHint
         }
         return result;
     }
+
+    /**
+     * Chroot a path under the new root
+     *
+     * @param  newRoot    the new root
+     * @param  toSanitize the path to sanitize and chroot
+     * @return            the chrooted path under the newRoot filesystem
+     */
+    public static Path chroot(Path newRoot, Path toSanitize) {
+        Objects.requireNonNull(newRoot);
+        Objects.requireNonNull(toSanitize);
+        List<String> sanitized = removeExtraCdUps(toSanitize);
+        return buildPath(newRoot, newRoot.getFileSystem(), sanitized);
+    }
+
+    /**
+     * Remove any extra directory ups from the Path
+     *
+     * @param  toSanitize the path to sanitize
+     * @return            the sanitized path
+     */
+    public static Path removeCdUpAboveRoot(Path toSanitize) {
+        List<String> sanitized = removeExtraCdUps(toSanitize);
+        return buildPath(toSanitize.getRoot(), toSanitize.getFileSystem(), 
sanitized);
+    }
+
+    private static List<String> removeExtraCdUps(Path toResolve) {
+        List<String> newNames = new ArrayList<>(toResolve.getNameCount());
+
+        int numCdUps = 0;
+        int numDirParts = 0;
+        for (int i = 0; i < toResolve.getNameCount(); i++) {
+            String name = toResolve.getName(i).toString();
+            if ("..".equals(name)) {
+                // If we have more cdups than dir parts, so we ignore the ".." 
to avoid jail escapes
+                if (numDirParts > numCdUps) {
+                    ++numCdUps;
+                    newNames.add(name);
+                }
+            } else {
+                // if the current directory is a part of the name, don't 
increment number of dir parts, as it doesn't
+                // add to the number of ".."s that can be present before the 
root
+                if (!".".equals(name)) {
+                    ++numDirParts;
+                }
+                newNames.add(name);
+            }
+        }
+        return newNames;
+    }
+
+    private static Path buildPath(Path root, FileSystem fs, List<String> 
namesList) {

Review Comment:
   Should be *public* in case users want/need to use it



##########
sshd-sftp/src/main/java/org/apache/sshd/sftp/server/AbstractSftpSubsystemHelper.java:
##########
@@ -2406,6 +2428,31 @@ protected NavigableMap<String, Object> 
resolveFileAttributes(
         });
     }
 
+    /**
+     * A utility function to validate that the directories leading up to a 
file are not symlinks when they are not
+     * supposed to be.
+     *
+     * @param  path                the file to check for symlink presence
+     * @param  neverFollowSymLinks whether to never follow symlinks in the 
parent paths
+     * @param  options             whether the file itself can be a symlink
+     * @return                     the status of the file
+     */
+    private static Boolean checkSymlinkState(Path path, boolean 
neverFollowSymLinks, LinkOption[] options) {
+        Boolean status = 
validateParentExistWithNoSymlinksIfNeverFollowSymlinks(path, 
neverFollowSymLinks);
+        if (!Boolean.FALSE.equals(status)) {
+            status = IoUtils.checkFileExists(path, options);
+        }
+        return status;
+    }
+
+    private static Boolean 
validateParentExistWithNoSymlinksIfNeverFollowSymlinks(Path path, boolean 
neverFollowSymLinks) {

Review Comment:
   Should be *public* in case users want/need to use it



##########
sshd-sftp/src/main/java/org/apache/sshd/sftp/server/AbstractSftpSubsystemHelper.java:
##########
@@ -2406,6 +2428,31 @@ protected NavigableMap<String, Object> 
resolveFileAttributes(
         });
     }
 
+    /**
+     * A utility function to validate that the directories leading up to a 
file are not symlinks when they are not
+     * supposed to be.
+     *
+     * @param  path                the file to check for symlink presence
+     * @param  neverFollowSymLinks whether to never follow symlinks in the 
parent paths
+     * @param  options             whether the file itself can be a symlink
+     * @return                     the status of the file
+     */
+    private static Boolean checkSymlinkState(Path path, boolean 
neverFollowSymLinks, LinkOption[] options) {

Review Comment:
   Should be *public* in case users want/need to use it



##########
sshd-common/src/main/java/org/apache/sshd/common/util/io/IoUtils.java:
##########
@@ -637,4 +677,75 @@ public static List<String> readAllLines(BufferedReader 
reader, int lineCountHint
         }
         return result;
     }
+
+    /**
+     * Chroot a path under the new root
+     *
+     * @param  newRoot    the new root
+     * @param  toSanitize the path to sanitize and chroot
+     * @return            the chrooted path under the newRoot filesystem
+     */
+    public static Path chroot(Path newRoot, Path toSanitize) {
+        Objects.requireNonNull(newRoot);
+        Objects.requireNonNull(toSanitize);
+        List<String> sanitized = removeExtraCdUps(toSanitize);
+        return buildPath(newRoot, newRoot.getFileSystem(), sanitized);
+    }
+
+    /**
+     * Remove any extra directory ups from the Path
+     *
+     * @param  toSanitize the path to sanitize
+     * @return            the sanitized path
+     */
+    public static Path removeCdUpAboveRoot(Path toSanitize) {
+        List<String> sanitized = removeExtraCdUps(toSanitize);
+        return buildPath(toSanitize.getRoot(), toSanitize.getFileSystem(), 
sanitized);
+    }
+
+    private static List<String> removeExtraCdUps(Path toResolve) {
+        List<String> newNames = new ArrayList<>(toResolve.getNameCount());
+
+        int numCdUps = 0;
+        int numDirParts = 0;
+        for (int i = 0; i < toResolve.getNameCount(); i++) {
+            String name = toResolve.getName(i).toString();
+            if ("..".equals(name)) {
+                // If we have more cdups than dir parts, so we ignore the ".." 
to avoid jail escapes
+                if (numDirParts > numCdUps) {
+                    ++numCdUps;
+                    newNames.add(name);
+                }
+            } else {
+                // if the current directory is a part of the name, don't 
increment number of dir parts, as it doesn't
+                // add to the number of ".."s that can be present before the 
root
+                if (!".".equals(name)) {
+                    ++numDirParts;
+                }
+                newNames.add(name);
+            }
+        }
+        return newNames;
+    }
+
+    private static Path buildPath(Path root, FileSystem fs, List<String> 
namesList) {
+        if (namesList.isEmpty()) {
+            return root == null ? fs.getPath(".") : root;
+        }
+
+        Path cleanedPathToResolve = buildPath(fs, namesList);
+        return root.resolve(cleanedPathToResolve);
+    }
+
+    private static Path buildPath(FileSystem fs, List<String> namesList) {

Review Comment:
   Should be *public* in case users want/need to use it



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: dev-unsubscr...@mina.apache.org

For queries about this service, please contact Infrastructure at:
us...@infra.apache.org


---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscr...@mina.apache.org
For additional commands, e-mail: dev-h...@mina.apache.org

Reply via email to