You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@mina.apache.org by gn...@apache.org on 2015/03/30 11:17:51 UTC

[2/6] mina-sshd git commit: [SSHD-438] Use common code to negotiate SFTP version

[SSHD-438] Use common code to negotiate SFTP version

Project: http://git-wip-us.apache.org/repos/asf/mina-sshd/repo
Commit: http://git-wip-us.apache.org/repos/asf/mina-sshd/commit/7ea07880
Tree: http://git-wip-us.apache.org/repos/asf/mina-sshd/tree/7ea07880
Diff: http://git-wip-us.apache.org/repos/asf/mina-sshd/diff/7ea07880

Branch: refs/heads/master
Commit: 7ea0788094726c193d2ecb2f5521b6479764b933
Parents: 2b640b0
Author: Guillaume Nodet <gn...@apache.org>
Authored: Mon Mar 30 10:50:50 2015 +0200
Committer: Guillaume Nodet <gn...@apache.org>
Committed: Mon Mar 30 10:50:50 2015 +0200

----------------------------------------------------------------------
 .../apache/sshd/server/sftp/SftpSubsystem.java  | 225 +++++++++++--------
 1 file changed, 134 insertions(+), 91 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/7ea07880/sshd-core/src/main/java/org/apache/sshd/server/sftp/SftpSubsystem.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/server/sftp/SftpSubsystem.java b/sshd-core/src/main/java/org/apache/sshd/server/sftp/SftpSubsystem.java
index ccf2b9c..92c043d 100644
--- a/sshd-core/src/main/java/org/apache/sshd/server/sftp/SftpSubsystem.java
+++ b/sshd-core/src/main/java/org/apache/sshd/server/sftp/SftpSubsystem.java
@@ -113,11 +113,22 @@ public class SftpSubsystem implements Command, Runnable, SessionAware, FileSyste
      */
     public static final String SFTP_VERSION = "sftp-version";
 
-    public static final int LOWER_SFTP_IMPL = 3; // Working implementation from v3
-    public static final int HIGHER_SFTP_IMPL = 6; //  .. up to
-    public static final String ALL_SFTP_IMPL = "3,4,5,6";
+    public static final int LOWER_SFTP_IMPL = SFTP_V3; // Working implementation from v3
+    public static final int HIGHER_SFTP_IMPL = SFTP_V6; //  .. up to
+    public static final String ALL_SFTP_IMPL;
     public static final int  MAX_PACKET_LENGTH = 1024 * 16;
 
+    static {
+        StringBuilder sb = new StringBuilder(2 * (1 + (HIGHER_SFTP_IMPL - LOWER_SFTP_IMPL)));
+        for (int v = LOWER_SFTP_IMPL; v <= HIGHER_SFTP_IMPL; v++) {
+            if (sb.length() > 0) {
+                sb.append(',');
+            }
+            sb.append(v);
+        }
+        ALL_SFTP_IMPL = sb.toString();
+    }
+
     private ExitCallback callback;
     private InputStream in;
     private OutputStream out;
@@ -574,19 +585,63 @@ public class SftpSubsystem implements Command, Runnable, SessionAware, FileSyste
     protected void doVersionSelect(Buffer buffer, int id) throws IOException {
         String ver = buffer.getString();
         log.debug("Received SSH_FXP_EXTENDED(version-select) (version={})", version);
-        if (Integer.toString(SFTP_V3).equals(ver)) {
-            version = SFTP_V3;
-        } else if (Integer.toString(SFTP_V4).equals(ver)) {
-            version = SFTP_V4;
-        } else if (Integer.toString(SFTP_V5).equals(ver)) {
-            version = SFTP_V5;
-        } else if (Integer.toString(SFTP_V6).equals(ver)) {
-            version = SFTP_V6;
-        } else {
-            sendStatus(id, SSH_FX_FAILURE, "Unsupported version " + ver);
-            return;
+        
+        if (GenericUtils.length(ver) == 1) {
+            char digit = ver.charAt(0);
+            if ((digit >= '0') && (digit <= '9')) {
+                int value = digit - '0';
+                String all = checkVersionCompatibility(id, value, SSH_FX_FAILURE);
+                if (GenericUtils.isEmpty(all)) {    // validation failed
+                    return;
+                }
+
+                version = value;
+                sendStatus(id, SSH_FX_OK, "");
+                return;
+            }
         }
-        sendStatus(id, SSH_FX_OK, "");
+
+        sendStatus(id, SSH_FX_FAILURE, "Unsupported version " + ver);
+    }
+
+    /**
+     * Checks if a proposed version is within supported range. <B>Note:</B>
+     * if the user forced a specific value via the {@link #SFTP_VERSION}
+     * property, then it is used to validate the proposed value
+     * @param id The SSH message ID to be used to send the failure message
+     * if required
+     * @param proposed The proposed version value
+     * @param failureOpcode The failure opcode to send if validation fails
+     * @return A {@link String} of comma separated values representing all
+     * the supported version - {@code null} if validation failed and an
+     * appropriate status message was sent
+     * @throws IOException If failed to send the failure status message
+     */
+    protected String checkVersionCompatibility(int id, int proposed, int failureOpcode) throws IOException {
+        int low = LOWER_SFTP_IMPL;
+        int hig = HIGHER_SFTP_IMPL;
+        String available = ALL_SFTP_IMPL;
+        // check if user wants to use a specific version
+        Integer sftpVersion = FactoryManagerUtils.getInteger(session, SFTP_VERSION);
+        if (sftpVersion != null) {
+            int forcedValue = sftpVersion.intValue();
+            if ((forcedValue < LOWER_SFTP_IMPL) || (forcedValue > HIGHER_SFTP_IMPL)) {
+                throw new IllegalStateException("Forced SFTP version (" + sftpVersion + ") not within supported values: " + available);
+            }
+            low = hig = sftpVersion.intValue();
+            available = sftpVersion.toString();
+        }
+
+        if (log.isTraceEnabled()) {
+            log.trace("checkVersionCompatibility(id={}) - proposed={}, available={}", new Object[] { id, proposed, available });
+        }
+
+        if ((proposed < low) || (proposed > hig)) {
+            sendStatus(id, failureOpcode, "Proposed version (" + proposed + ") not in supported range: " + available);
+            return null;
+        }
+
+        return available;
     }
 
     protected void doBlock(Buffer buffer, int id) throws IOException {
@@ -1119,6 +1174,11 @@ public class SftpSubsystem implements Command, Runnable, SessionAware, FileSyste
         if (log.isDebugEnabled()) {
             log.debug("Received SSH_FXP_INIT (version={})", id);
         }
+
+        String all = checkVersionCompatibility(id, id, SSH_FX_OP_UNSUPPORTED);
+        if (GenericUtils.isEmpty(all)) { // i.e. validation failed
+            return;
+        }
         version = id;
         while (buffer.available() > 0) {
             String name = buffer.getString();
@@ -1126,86 +1186,69 @@ public class SftpSubsystem implements Command, Runnable, SessionAware, FileSyste
             extensions.put(name, data);
         }
 
-        int low = LOWER_SFTP_IMPL;
-        int hig = HIGHER_SFTP_IMPL;
-        String all = ALL_SFTP_IMPL;
+        buffer.clear();
+        buffer.putByte((byte) SSH_FXP_VERSION);
+        buffer.putInt(version);
 
-        // check if specific version forced
-        Integer sftpVersion = FactoryManagerUtils.getInteger(session, SFTP_VERSION);
-        if (sftpVersion != null) {
-            low = hig = sftpVersion.intValue();
-            all = sftpVersion.toString();
-        }
-
-        if (version >= low) {
-            version = Math.min(version, hig);
-            buffer.clear();
-            buffer.putByte((byte) SSH_FXP_VERSION);
-            buffer.putInt(version);
-
-            // newline
-            buffer.putString("newline");
-            buffer.putString(System.getProperty("line.separator"));
-
-            // versions
-            buffer.putString("versions");
-            buffer.putString(all);
-
-            // supported
-            buffer.putString("supported");
-            buffer.putInt(5 * 4); // length of 5 integers
-            // supported-attribute-mask
-            buffer.putInt(SSH_FILEXFER_ATTR_SIZE | SSH_FILEXFER_ATTR_PERMISSIONS
-                    | SSH_FILEXFER_ATTR_ACCESSTIME | SSH_FILEXFER_ATTR_CREATETIME
-                    | SSH_FILEXFER_ATTR_MODIFYTIME | SSH_FILEXFER_ATTR_OWNERGROUP
-                    | SSH_FILEXFER_ATTR_BITS);
-            // TODO: supported-attribute-bits
-            buffer.putInt(0);
-            // supported-open-flags
-            buffer.putInt(SSH_FXF_READ | SSH_FXF_WRITE | SSH_FXF_APPEND
-                    | SSH_FXF_CREAT | SSH_FXF_TRUNC | SSH_FXF_EXCL);
-            // TODO: supported-access-mask
-            buffer.putInt(0);
-            // max-read-size
-            buffer.putInt(0);
+        // newline
+        buffer.putString("newline");
+        buffer.putString(System.getProperty("line.separator"));
 
-            // supported2
-            buffer.putString("supported2");
-            buffer.putInt(8 * 4); // length of 7 integers + 2 shorts
-            // supported-attribute-mask
-            buffer.putInt(SSH_FILEXFER_ATTR_SIZE | SSH_FILEXFER_ATTR_PERMISSIONS
-                    | SSH_FILEXFER_ATTR_ACCESSTIME | SSH_FILEXFER_ATTR_CREATETIME
-                    | SSH_FILEXFER_ATTR_MODIFYTIME | SSH_FILEXFER_ATTR_OWNERGROUP
-                    | SSH_FILEXFER_ATTR_BITS);
-            // TODO: supported-attribute-bits
-            buffer.putInt(0);
-            // supported-open-flags
-            buffer.putInt(SSH_FXF_ACCESS_DISPOSITION | SSH_FXF_APPEND_DATA);
-            // TODO: supported-access-mask
-            buffer.putInt(0);
-            // max-read-size
-            buffer.putInt(0);
-            // supported-open-block-vector
-            buffer.putShort(0);
-            // supported-block-vector
-            buffer.putShort(0);
-            // attrib-extension-count
-            buffer.putInt(0);
-            // extension-count
-            buffer.putInt(0);
+        // versions
+        buffer.putString("versions");
+        buffer.putString(all);
 
-                /*
-                buffer.putString("acl-supported");
-                buffer.putInt(4);
-                // capabilities
-                buffer.putInt(0);
-                */
+        // supported
+        buffer.putString("supported");
+        buffer.putInt(5 * 4); // length of 5 integers
+        // supported-attribute-mask
+        buffer.putInt(SSH_FILEXFER_ATTR_SIZE | SSH_FILEXFER_ATTR_PERMISSIONS
+                | SSH_FILEXFER_ATTR_ACCESSTIME | SSH_FILEXFER_ATTR_CREATETIME
+                | SSH_FILEXFER_ATTR_MODIFYTIME | SSH_FILEXFER_ATTR_OWNERGROUP
+                | SSH_FILEXFER_ATTR_BITS);
+        // TODO: supported-attribute-bits
+        buffer.putInt(0);
+        // supported-open-flags
+        buffer.putInt(SSH_FXF_READ | SSH_FXF_WRITE | SSH_FXF_APPEND
+                | SSH_FXF_CREAT | SSH_FXF_TRUNC | SSH_FXF_EXCL);
+        // TODO: supported-access-mask
+        buffer.putInt(0);
+        // max-read-size
+        buffer.putInt(0);
 
-            send(buffer);
-        } else {
-            // We only support version >= 3 (Version 1 and 2 are not common)
-            sendStatus(id, SSH_FX_OP_UNSUPPORTED, "SFTP server only support versions " + all);
-        }
+        // supported2
+        buffer.putString("supported2");
+        buffer.putInt(8 * 4); // length of 7 integers + 2 shorts
+        // supported-attribute-mask
+        buffer.putInt(SSH_FILEXFER_ATTR_SIZE | SSH_FILEXFER_ATTR_PERMISSIONS
+                | SSH_FILEXFER_ATTR_ACCESSTIME | SSH_FILEXFER_ATTR_CREATETIME
+                | SSH_FILEXFER_ATTR_MODIFYTIME | SSH_FILEXFER_ATTR_OWNERGROUP
+                | SSH_FILEXFER_ATTR_BITS);
+        // TODO: supported-attribute-bits
+        buffer.putInt(0);
+        // supported-open-flags
+        buffer.putInt(SSH_FXF_ACCESS_DISPOSITION | SSH_FXF_APPEND_DATA);
+        // TODO: supported-access-mask
+        buffer.putInt(0);
+        // max-read-size
+        buffer.putInt(0);
+        // supported-open-block-vector
+        buffer.putShort(0);
+        // supported-block-vector
+        buffer.putShort(0);
+        // attrib-extension-count
+        buffer.putInt(0);
+        // extension-count
+        buffer.putInt(0);
+
+        /*
+        buffer.putString("acl-supported");
+        buffer.putInt(4);
+        // capabilities
+        buffer.putInt(0);
+        */
+
+        send(buffer);
     }
 
     protected void sendHandle(int id, String handle) throws IOException {