You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@mina.apache.org by lg...@apache.org on 2015/07/01 15:20:24 UTC

mina-sshd git commit: [SSHD-330] Handshake fails (wrong shared secret) 1 out of 256 times

Repository: mina-sshd
Updated Branches:
  refs/heads/master a6e2bf9e4 -> df8324bcf


[SSHD-330] Handshake fails (wrong shared secret) 1 out of 256 times

* Improved AbstractDH#stripLeadingZeros to clone the array only if 1st byte is non-zero


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

Branch: refs/heads/master
Commit: df8324bcfef82c7dcee2b4e2f0e2bb34aa5a4add
Parents: a6e2bf9
Author: Lyor Goldstein <lg...@vmware.com>
Authored: Wed Jul 1 16:20:05 2015 +0300
Committer: Lyor Goldstein <lg...@vmware.com>
Committed: Wed Jul 1 16:20:05 2015 +0300

----------------------------------------------------------------------
 .../org/apache/sshd/common/kex/AbstractDH.java  |  41 ++-
 .../auth/password/PasswordAuthenticator.java    |   2 +-
 .../server/subsystem/sftp/SftpSubsystem.java    | 314 ++++++++++---------
 .../apache/sshd/common/kex/AbstractDHTest.java  |  67 ++++
 4 files changed, 258 insertions(+), 166 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/df8324bc/sshd-core/src/main/java/org/apache/sshd/common/kex/AbstractDH.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/kex/AbstractDH.java b/sshd-core/src/main/java/org/apache/sshd/common/kex/AbstractDH.java
index 4f2b565..d2a3092 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/kex/AbstractDH.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/kex/AbstractDH.java
@@ -21,10 +21,10 @@ package org.apache.sshd.common.kex;
 import java.math.BigInteger;
 
 import org.apache.sshd.common.digest.Digest;
+import org.apache.sshd.common.util.GenericUtils;
 
 /**
  * Base class for the Diffie-Hellman key agreement.
- * 
  */
 public abstract class AbstractDH {
 
@@ -51,18 +51,33 @@ public abstract class AbstractDH {
 
     public abstract Digest getHash() throws Exception;
 
-    // The shared secret returned by KeyAgreement.generateSecret() is
-    // a byte array, which can (by chance, roughly 1 out of 256 times)
-    // begin with zero byte (some JCE providers might strip this, though).
-    // In SSH, the shared secret is an integer, so we need to strip
-    // the leading zero(es).
-    protected static byte[] stripLeadingZeroes(byte[] x) {
-        int i = 0;
-        while ((i < x.length - 1) && (x[i] == 0)) {
-            i++;
+    /**
+     * The shared secret returned by {@link javax.crypto.KeyAgreement#generateSecret()}
+     * is a byte array, which can (by chance, roughly 1 out of 256 times) begin
+     * with zero byte (some JCE providers might strip this, though). In SSH,
+     * the shared secret is an integer, so we need to strip the leading zero(es).
+     * @param x The original array
+     * @return An (possibly) sub-array guaranteed to start with a non-zero byte 
+     * @see <A HREF="https://issues.apache.org/jira/browse/SSHD-330">SSHD-330</A>
+     * @throws IllegalArgumentException If all zeroes array
+     */
+    public static byte[] stripLeadingZeroes(byte[] x) {
+        int length = GenericUtils.length(x);
+        for (int i = 0; i < x.length; i++) {
+            if (x[i] == 0) {
+                continue;
+            }
+
+            if (i == 0) {   // 1st byte is non-zero so nothing to do
+                return x;
+            }
+
+            byte[] ret = new byte[length - i];
+            System.arraycopy(x, i, ret, 0, ret.length);
+            return ret;
         }
-        byte[] ret = new byte[x.length - i];
-        System.arraycopy(x, i, ret, 0, ret.length);
-        return ret;
+        
+        // all zeroes
+        throw new IllegalArgumentException("No non-zero values in generated secret");
     }
 }

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/df8324bc/sshd-core/src/main/java/org/apache/sshd/server/auth/password/PasswordAuthenticator.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/server/auth/password/PasswordAuthenticator.java b/sshd-core/src/main/java/org/apache/sshd/server/auth/password/PasswordAuthenticator.java
index 8139bef..20bdef6 100644
--- a/sshd-core/src/main/java/org/apache/sshd/server/auth/password/PasswordAuthenticator.java
+++ b/sshd-core/src/main/java/org/apache/sshd/server/auth/password/PasswordAuthenticator.java
@@ -29,7 +29,7 @@ public interface PasswordAuthenticator {
      * Check the validity of a password.
      * @param username The username credential
      * @param password The provided password
-     * @param session The {@link ServerSession} attemtpting the authentication
+     * @param session The {@link ServerSession} attempting the authentication
      * @return {@code true} indicating if authentication succeeded
      */
     boolean authenticate(String username, String password, ServerSession session);

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/df8324bc/sshd-core/src/main/java/org/apache/sshd/server/subsystem/sftp/SftpSubsystem.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/server/subsystem/sftp/SftpSubsystem.java b/sshd-core/src/main/java/org/apache/sshd/server/subsystem/sftp/SftpSubsystem.java
index c6baa5c..c3a3efa 100644
--- a/sshd-core/src/main/java/org/apache/sshd/server/subsystem/sftp/SftpSubsystem.java
+++ b/sshd-core/src/main/java/org/apache/sshd/server/subsystem/sftp/SftpSubsystem.java
@@ -28,7 +28,6 @@ import java.io.FileNotFoundException;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
-import java.nio.BufferUnderflowException;
 import java.nio.ByteBuffer;
 import java.nio.channels.FileChannel;
 import java.nio.channels.FileLock;
@@ -230,22 +229,22 @@ public class SftpSubsystem extends AbstractLoggingBean implements Command, Runna
                 options.add(StandardOpenOption.WRITE);
             }
             switch (flags & SSH_FXF_ACCESS_DISPOSITION) {
-            case SSH_FXF_CREATE_NEW:
-                options.add(StandardOpenOption.CREATE_NEW);
-                break;
-            case SSH_FXF_CREATE_TRUNCATE:
-                options.add(StandardOpenOption.CREATE);
-                options.add(StandardOpenOption.TRUNCATE_EXISTING);
-                break;
-            case SSH_FXF_OPEN_EXISTING:
-                break;
-            case SSH_FXF_OPEN_OR_CREATE:
-                options.add(StandardOpenOption.CREATE);
-                break;
-            case SSH_FXF_TRUNCATE_EXISTING:
-                options.add(StandardOpenOption.TRUNCATE_EXISTING);
-                break;
-            default:    // ignored
+                case SSH_FXF_CREATE_NEW:
+                    options.add(StandardOpenOption.CREATE_NEW);
+                    break;
+                case SSH_FXF_CREATE_TRUNCATE:
+                    options.add(StandardOpenOption.CREATE);
+                    options.add(StandardOpenOption.TRUNCATE_EXISTING);
+                    break;
+                case SSH_FXF_OPEN_EXISTING:
+                    break;
+                case SSH_FXF_OPEN_OR_CREATE:
+                    options.add(StandardOpenOption.CREATE);
+                    break;
+                case SSH_FXF_TRUNCATE_EXISTING:
+                    options.add(StandardOpenOption.TRUNCATE_EXISTING);
+                    break;
+                default:    // ignored
             }
             if ((flags & SSH_FXF_APPEND_DATA) != 0) {
                 options.add(StandardOpenOption.APPEND);
@@ -465,7 +464,7 @@ public class SftpSubsystem extends AbstractLoggingBean implements Command, Runna
         int id = buffer.getInt();
         if (log.isDebugEnabled()) {
             log.debug("process(length={}, type={}, id={})",
-                      new Object[] { Integer.valueOf(length), Integer.valueOf(type), Integer.valueOf(id) });
+                      Integer.valueOf(length), Integer.valueOf(type), Integer.valueOf(id));
         }
 
         switch (type) {
@@ -547,27 +546,28 @@ public class SftpSubsystem extends AbstractLoggingBean implements Command, Runna
     protected void doExtended(Buffer buffer, int id) throws IOException {
         String extension = buffer.getString();
         switch (extension) {
-        case "text-seek":
-            doTextSeek(buffer, id);
-            break;
-        case "version-select":
-            doVersionSelect(buffer, id);
-            break;
-        default:
-            log.info("Received unsupported SSH_FXP_EXTENDED({})", extension);
-            sendStatus(id, SSH_FX_OP_UNSUPPORTED, "Command SSH_FXP_EXTENDED(" + extension + ") is unsupported or not implemented");
-            break;
+            case "text-seek":
+                doTextSeek(buffer, id);
+                break;
+            case "version-select":
+                doVersionSelect(buffer, id);
+                break;
+            default:
+                log.info("Received unsupported SSH_FXP_EXTENDED({})", extension);
+                sendStatus(id, SSH_FX_OP_UNSUPPORTED, "Command SSH_FXP_EXTENDED(" + extension + ") is unsupported or not implemented");
+                break;
         }
     }
 
     protected void doTextSeek(Buffer buffer, int id) throws IOException {
         String handle = buffer.getString();
         long line = buffer.getLong();
+        Handle h = handles.get(handle);
         if (log.isDebugEnabled()) {
-            log.debug("Received SSH_FXP_EXTENDED(text-seek) (handle={}, line={})", handle, Long.valueOf(line));
+            log.debug("Received SSH_FXP_EXTENDED(text-seek) (handle={}[{}], line={})", handle, h, Long.valueOf(line));
         }
 
-        // TODO : implement text-seek
+        // TODO : implement text-seek - see https://tools.ietf.org/html/draft-ietf-secsh-filexfer-03#section-6.3
         sendStatus(id, SSH_FX_OP_UNSUPPORTED, "Command SSH_FXP_EXTENDED(text-seek) is unsupported or not implemented");
     }
 
@@ -642,17 +642,18 @@ public class SftpSubsystem extends AbstractLoggingBean implements Command, Runna
         long length = buffer.getLong();
         int mask = buffer.getInt();
         
-        if (log.isDebugEnabled()) {
-            log.debug("Received SSH_FXP_BLOCK (handle={}, offset={}, length={}, mask=0x{})",
-                      new Object[] { handle, Long.valueOf(offset), Long.valueOf(length), Integer.toHexString(mask) });
-        }
-
         try {
             Handle p = handles.get(handle);
+            if (log.isDebugEnabled()) {
+                log.debug("Received SSH_FXP_BLOCK (handle={}[{}], offset={}, length={}, mask=0x{})",
+                          handle, p, Long.valueOf(offset), Long.valueOf(length), Integer.toHexString(mask));
+            }
+
             if (!(p instanceof FileHandle)) {
                 sendStatus(id, SSH_FX_INVALID_HANDLE, handle);
                 return;
             }
+
             FileHandle fileHandle = (FileHandle) p;
             fileHandle.lock(offset, length, mask);
             sendStatus(id, SSH_FX_OK, "");
@@ -665,17 +666,18 @@ public class SftpSubsystem extends AbstractLoggingBean implements Command, Runna
         String handle = buffer.getString();
         long offset = buffer.getLong();
         long length = buffer.getLong();
-        if (log.isDebugEnabled()) {
-            log.debug("Received SSH_FXP_UNBLOCK (handle={}, offset={}, length={})",
-                      new Object[] { handle, Long.valueOf(offset), Long.valueOf(length) });
-        }
-
         try {
             Handle p = handles.get(handle);
+            if (log.isDebugEnabled()) {
+                log.debug("Received SSH_FXP_UNBLOCK (handle={}[{}], offset={}, length={})",
+                          handle, p, Long.valueOf(offset), Long.valueOf(length));
+            }
+
             if (!(p instanceof FileHandle)) {
                 sendStatus(id, SSH_FX_INVALID_HANDLE, handle);
                 return;
             }
+
             FileHandle fileHandle = (FileHandle) p;
             boolean found = fileHandle.unlock(offset, length);
             sendStatus(id, found ? SSH_FX_OK : SSH_FX_NO_MATCHING_BYTE_RANGE_LOCK, "");
@@ -690,7 +692,7 @@ public class SftpSubsystem extends AbstractLoggingBean implements Command, Runna
         boolean symLink = buffer.getBoolean();
         if (log.isDebugEnabled()) {
             log.debug("Received SSH_FXP_LINK (linkpath={}, targetpath={}, symlink={})",
-                      new Object[] { linkpath, targetpath, Boolean.valueOf(symLink) });
+                      linkpath, targetpath, Boolean.valueOf(symLink));
         }
 
         try {
@@ -748,8 +750,9 @@ public class SftpSubsystem extends AbstractLoggingBean implements Command, Runna
         }
         if (log.isDebugEnabled()) {
             log.debug("Received SSH_FXP_RENAME (oldPath={}, newPath={}, flags=0x{})",
-                       new Object[] { oldPath, newPath, Integer.toHexString(flags) });
+                       oldPath, newPath, Integer.toHexString(flags));
         }
+
         try {
             List<CopyOption> opts = new ArrayList<>();
             if ((flags & SSH_FXP_RENAME_ATOMIC) != 0) {
@@ -774,7 +777,7 @@ public class SftpSubsystem extends AbstractLoggingBean implements Command, Runna
             flags = buffer.getInt();
         }
         if (log.isDebugEnabled()) {
-            log.debug("Received SSH_FXP_STAT (path={}, flags={})", path, "0x" + Integer.toHexString(flags));
+            log.debug("Received SSH_FXP_STAT (path={}, flags=0x{})", path, Integer.toHexString(flags));
         }
         try {
             Path p = resolveFile(path);
@@ -877,7 +880,7 @@ public class SftpSubsystem extends AbstractLoggingBean implements Command, Runna
         String path = buffer.getString();
         Map<String, Object> attrs = readAttrs(buffer);
 
-        log.debug("Received SSH_FXP_MKDIR (path={})", path);
+        log.debug("Received SSH_FXP_MKDIR (path={}, attrs={})", path, attrs);
         // attrs
         try {
             Path            p = resolveFile(path);
@@ -927,8 +930,9 @@ public class SftpSubsystem extends AbstractLoggingBean implements Command, Runna
 
     protected void doReadDir(Buffer buffer, int id) throws IOException {
         String handle = buffer.getString();
-        log.debug("Received SSH_FXP_READDIR (handle={})", handle);
         Handle p = handles.get(handle);
+        log.debug("Received SSH_FXP_READDIR (handle={}[{}])", handle, p);
+
         try {
             if (!(p instanceof DirectoryHandle)) {
                 sendStatus(id, SSH_FX_INVALID_HANDLE, handle);
@@ -1041,7 +1045,7 @@ public class SftpSubsystem extends AbstractLoggingBean implements Command, Runna
             flags = buffer.getInt();
         }
         if (log.isDebugEnabled()) {
-            log.debug("Received SSH_FXP_FSTAT (handle={}, flags={})", handle, "0x" + Integer.toHexString(flags));
+            log.debug("Received SSH_FXP_FSTAT (handle={}, flags=0x{})", handle, Integer.toHexString(flags));
         }
         try {
             Handle p = handles.get(handle);
@@ -1062,7 +1066,7 @@ public class SftpSubsystem extends AbstractLoggingBean implements Command, Runna
             flags = buffer.getInt();
         }
         if (log.isDebugEnabled()) {
-            log.debug("Received SSH_FXP_LSTAT (path={}, flags={})", path, "0x" + Integer.toHexString(flags));
+            log.debug("Received SSH_FXP_LSTAT (path={}, flags=0x{})", path, Integer.toHexString(flags));
         }
         try {
             Path p = resolveFile(path);
@@ -1077,19 +1081,23 @@ public class SftpSubsystem extends AbstractLoggingBean implements Command, Runna
         long offset = buffer.getLong();
         int length = buffer.getInt();
         if (length < 0) {
-            throw new IllegalStateException();
+            throw new IllegalStateException("Bad length (" + length + ") for writing to " + handle);
         }
-        if (buffer.available() < length) {
-            throw new BufferUnderflowException();
+
+        int remaining = buffer.available();
+        if (remaining < length) {
+            throw new IllegalStateException("Not enough buffer data for writing to " + handle + ": required=" + length + ", available=" + remaining);
         }
+
         byte[] data = buffer.array();
         int doff = buffer.rpos();
-        if (log.isDebugEnabled()) {
-            log.debug("Received SSH_FXP_WRITE (handle={}, offset={}, data=byte[{}])",
-                      new Object[] { handle, Long.valueOf(offset), Integer.valueOf(length) });
-        }
         try {
             Handle p = handles.get(handle);
+            if (log.isDebugEnabled()) {
+                log.debug("Received SSH_FXP_WRITE (handle={}[{}], offset={}, data=byte[{}])",
+                          handle, p, Long.valueOf(offset), Integer.valueOf(length));
+            }
+
             if (!(p instanceof FileHandle)) {
                 sendStatus(id, SSH_FX_INVALID_HANDLE, handle);
             } else {
@@ -1106,12 +1114,13 @@ public class SftpSubsystem extends AbstractLoggingBean implements Command, Runna
         String handle = buffer.getString();
         long offset = buffer.getLong();
         int len = buffer.getInt();
-        if (log.isDebugEnabled()) {
-            log.debug("Received SSH_FXP_READ (handle={}, offset={}, length={})",
-                      new Object[]{handle, Long.valueOf(offset), Integer.valueOf(len) });
-        }
         try {
             Handle p = handles.get(handle);
+            if (log.isDebugEnabled()) {
+                log.debug("Received SSH_FXP_READ (handle={}[{}], offset={}, length={})",
+                          handle, p, Long.valueOf(offset), Integer.valueOf(len));
+            }
+
             if (!(p instanceof FileHandle)) {
                 sendStatus(id, SSH_FX_INVALID_HANDLE, handle);
             } else {
@@ -1138,9 +1147,9 @@ public class SftpSubsystem extends AbstractLoggingBean implements Command, Runna
 
     protected void doClose(Buffer buffer, int id) throws IOException {
         String handle = buffer.getString();
-        log.debug("Received SSH_FXP_CLOSE (handle={})", handle);
         try {
             Handle h = handles.get(handle);
+            log.debug("Received SSH_FXP_CLOSE (handle={}[{}])", handle, h);
             if (h == null) {
                 sendStatus(id, SSH_FX_INVALID_HANDLE, handle, "");
             } else {
@@ -1154,9 +1163,10 @@ public class SftpSubsystem extends AbstractLoggingBean implements Command, Runna
     }
 
     protected void doOpen(Buffer buffer, int id) throws IOException {
+        int curHandleCount = handles.size();
         int maxHandleCount = FactoryManagerUtils.getIntProperty(session, MAX_OPEN_HANDLES_PER_SESSION, Integer.MAX_VALUE);
-        if (handles.size() > maxHandleCount) {
-            sendStatus(id, SSH_FX_FAILURE, "Too many open handles");
+        if (curHandleCount > maxHandleCount) {
+            sendStatus(id, SSH_FX_FAILURE, "Too many open handles: current=" + curHandleCount + ", max.=" + maxHandleCount);
             return;
         }
 
@@ -1170,16 +1180,16 @@ public class SftpSubsystem extends AbstractLoggingBean implements Command, Runna
             int flags = pflags;
             pflags = 0;
             switch (flags & (SSH_FXF_READ | SSH_FXF_WRITE)) {
-            case SSH_FXF_READ:
-                access |= ACE4_READ_DATA | ACE4_READ_ATTRIBUTES;
-                break;
-            case SSH_FXF_WRITE:
-                access |= ACE4_WRITE_DATA | ACE4_WRITE_ATTRIBUTES;
-                break;
-            default:
-                access |= ACE4_READ_DATA | ACE4_READ_ATTRIBUTES;
-                access |= ACE4_WRITE_DATA | ACE4_WRITE_ATTRIBUTES;
-                break;
+                case SSH_FXF_READ:
+                    access |= ACE4_READ_DATA | ACE4_READ_ATTRIBUTES;
+                    break;
+                case SSH_FXF_WRITE:
+                    access |= ACE4_WRITE_DATA | ACE4_WRITE_ATTRIBUTES;
+                    break;
+                default:
+                    access |= ACE4_READ_DATA | ACE4_READ_ATTRIBUTES;
+                    access |= ACE4_WRITE_DATA | ACE4_WRITE_ATTRIBUTES;
+                    break;
             }
             if ((flags & SSH_FXF_APPEND) != 0) {
                 access |= ACE4_APPEND_DATA;
@@ -1204,7 +1214,7 @@ public class SftpSubsystem extends AbstractLoggingBean implements Command, Runna
         Map<String, Object> attrs = readAttrs(buffer);
         if (log.isDebugEnabled()) {
             log.debug("Received SSH_FXP_OPEN (path={}, access=0x{}, pflags=0x{}, attrs={})",
-                      new Object[]{path, Integer.toHexString(access), Integer.toHexString(pflags), attrs});
+                      path, Integer.toHexString(access), Integer.toHexString(pflags), attrs);
         }
         try {
             Path file = resolveFile(path);
@@ -1493,34 +1503,34 @@ public class SftpSubsystem extends AbstractLoggingBean implements Command, Runna
         if (perms != null) {
             for (PosixFilePermission p : perms) {
                 switch (p) {
-                case OWNER_READ:
-                    pf |= S_IRUSR;
-                    break;
-                case OWNER_WRITE:
-                    pf |= S_IWUSR;
-                    break;
-                case OWNER_EXECUTE:
-                    pf |= S_IXUSR;
-                    break;
-                case GROUP_READ:
-                    pf |= S_IRGRP;
-                    break;
-                case GROUP_WRITE:
-                    pf |= S_IWGRP;
-                    break;
-                case GROUP_EXECUTE:
-                    pf |= S_IXGRP;
-                    break;
-                case OTHERS_READ:
-                    pf |= S_IROTH;
-                    break;
-                case OTHERS_WRITE:
-                    pf |= S_IWOTH;
-                    break;
-                case OTHERS_EXECUTE:
-                    pf |= S_IXOTH;
-                    break;
-                default: // ignored
+                    case OWNER_READ:
+                        pf |= S_IRUSR;
+                        break;
+                    case OWNER_WRITE:
+                        pf |= S_IWUSR;
+                        break;
+                    case OWNER_EXECUTE:
+                        pf |= S_IXUSR;
+                        break;
+                    case GROUP_READ:
+                        pf |= S_IRGRP;
+                        break;
+                    case GROUP_WRITE:
+                        pf |= S_IWGRP;
+                        break;
+                    case GROUP_EXECUTE:
+                        pf |= S_IXGRP;
+                        break;
+                    case OTHERS_READ:
+                        pf |= S_IROTH;
+                        break;
+                    case OTHERS_WRITE:
+                        pf |= S_IWOTH;
+                        break;
+                    case OTHERS_EXECUTE:
+                        pf |= S_IXOTH;
+                        break;
+                    default: // ignored
                 }
             }
         }
@@ -1715,47 +1725,47 @@ public class SftpSubsystem extends AbstractLoggingBean implements Command, Runna
             String view = null;
             Object value = attributes.get(attribute);
             switch (attribute) {
-            case "size": {
-                long newSize = ((Number) value).longValue();
-                try (FileChannel channel = FileChannel.open(file, StandardOpenOption.WRITE)) {
-                    channel.truncate(newSize);
-                }
-                continue;
-            }
-            case "uid":
-                view = "unix";
-                break;
-            case "gid":
-                view = "unix";
-                break;
-            case "owner":
-                view = "posix";
-                value = toUser(file, (UserPrincipal) value);
-                break;
-            case "group":
-                view = "posix";
-                value = toGroup(file, (GroupPrincipal) value);
-                break;
-            case "permissions":
-                if (OsUtils.isWin32()) {
-                    @SuppressWarnings("unchecked")
-                    Collection<PosixFilePermission> perms = (Collection<PosixFilePermission>) value;
-                    IoUtils.setPermissionsToFile(file.toFile(), perms);
+                case "size": {
+                    long newSize = ((Number) value).longValue();
+                    try (FileChannel channel = FileChannel.open(file, StandardOpenOption.WRITE)) {
+                        channel.truncate(newSize);
+                    }
                     continue;
                 }
-                view = "posix";
-                break;
-
-            case "creationTime":
-                view = "basic";
-                break;
-            case "lastModifiedTime":
-                view = "basic";
-                break;
-            case "lastAccessTime":
-                view = "basic";
-                break;
-            default:    // ignored
+                case "uid":
+                    view = "unix";
+                    break;
+                case "gid":
+                    view = "unix";
+                    break;
+                case "owner":
+                    view = "posix";
+                    value = toUser(file, (UserPrincipal) value);
+                    break;
+                case "group":
+                    view = "posix";
+                    value = toGroup(file, (GroupPrincipal) value);
+                    break;
+                case "permissions":
+                    if (OsUtils.isWin32()) {
+                        @SuppressWarnings("unchecked")
+                        Collection<PosixFilePermission> perms = (Collection<PosixFilePermission>) value;
+                        IoUtils.setPermissionsToFile(file.toFile(), perms);
+                        continue;
+                    }
+                    view = "posix";
+                    break;
+    
+                case "creationTime":
+                    view = "basic";
+                    break;
+                case "lastModifiedTime":
+                    view = "basic";
+                    break;
+                case "lastAccessTime":
+                    view = "basic";
+                    break;
+                default:    // ignored
             }
             if (view != null && value != null) {
                 try {
@@ -1873,19 +1883,19 @@ public class SftpSubsystem extends AbstractLoggingBean implements Command, Runna
         if (version >= SFTP_V4) {
             byte type = buffer.getByte();
             switch (type) {
-            case SSH_FILEXFER_TYPE_REGULAR:
-                attrs.put("isRegular", Boolean.TRUE);
-                break;
-            case SSH_FILEXFER_TYPE_DIRECTORY:
-                attrs.put("isDirectory", Boolean.TRUE);
-                break;
-            case SSH_FILEXFER_TYPE_SYMLINK:
-                attrs.put("isSymbolicLink", Boolean.TRUE);
-                break;
-            case SSH_FILEXFER_TYPE_UNKNOWN:
-                attrs.put("isOther", Boolean.TRUE);
-                break;
-            default:    // ignored
+                case SSH_FILEXFER_TYPE_REGULAR:
+                    attrs.put("isRegular", Boolean.TRUE);
+                    break;
+                case SSH_FILEXFER_TYPE_DIRECTORY:
+                    attrs.put("isDirectory", Boolean.TRUE);
+                    break;
+                case SSH_FILEXFER_TYPE_SYMLINK:
+                    attrs.put("isSymbolicLink", Boolean.TRUE);
+                    break;
+                case SSH_FILEXFER_TYPE_UNKNOWN:
+                    attrs.put("isOther", Boolean.TRUE);
+                    break;
+                default:    // ignored
             }
         }
         if ((flags & SSH_FILEXFER_ATTR_SIZE) != 0) {

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/df8324bc/sshd-core/src/test/java/org/apache/sshd/common/kex/AbstractDHTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/common/kex/AbstractDHTest.java b/sshd-core/src/test/java/org/apache/sshd/common/kex/AbstractDHTest.java
new file mode 100644
index 0000000..22b61c2
--- /dev/null
+++ b/sshd-core/src/test/java/org/apache/sshd/common/kex/AbstractDHTest.java
@@ -0,0 +1,67 @@
+/*
+ * 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.kex;
+
+import java.util.Arrays;
+
+import org.apache.sshd.util.BaseTestSupport;
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.runners.MethodSorters;
+
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+public class AbstractDHTest extends BaseTestSupport {
+    public AbstractDHTest() {
+        super();
+    }
+
+    @Test
+    public void testStripLeadingZeroes() {
+        byte[] data = { 3, 7, 7, 3, 4, 7 };
+        for (int index = 1; index <= data.length; index++) {
+            assertSame("Unexpected sub-array generation for " + Arrays.toString(data), data, AbstractDH.stripLeadingZeroes(data));
+            if (index < data.length) {
+                data[index] = 0;
+            }
+        }
+        
+        Arrays.fill(data, (byte) 0);
+        try {
+            byte[] stripped = AbstractDH.stripLeadingZeroes(data);
+            fail("Unexpected success for all zeroes data: " + Arrays.toString(stripped));
+        } catch(IllegalArgumentException expected) {
+            // ignored
+        }
+
+        for (int index = data.length - 1; index > 0; index--) {
+            data[index] = (byte) index;
+            
+            byte[] stripped = AbstractDH.stripLeadingZeroes(data);
+            String ds = Arrays.toString(data), ss = Arrays.toString(stripped); 
+            assertEquals("Mismatched stripped (" + ss + ") length for " + ds, data.length - index, stripped.length);
+            for (int i=index, j=0; j < stripped.length; i++, j++) {
+                if (data[i] != stripped[j]) {
+                    fail("Mismatched values at stripped index = " + j + ": data=" + ds + ", stripped=" + ss);
+                }
+            }
+        }
+        
+    }
+}