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 2018/11/18 09:32:59 UTC
[1/5] mina-sshd git commit: Add more detailed information about file
upload/download when using 'get/put' in SftpCommandMain with verbose flag set
Repository: mina-sshd
Updated Branches:
refs/heads/master a6dffd5ac -> e88a08326
Add more detailed information about file upload/download when using 'get/put' in SftpCommandMain with verbose flag set
Project: http://git-wip-us.apache.org/repos/asf/mina-sshd/repo
Commit: http://git-wip-us.apache.org/repos/asf/mina-sshd/commit/e88a0832
Tree: http://git-wip-us.apache.org/repos/asf/mina-sshd/tree/e88a0832
Diff: http://git-wip-us.apache.org/repos/asf/mina-sshd/diff/e88a0832
Branch: refs/heads/master
Commit: e88a08326716e1ebbe693ede3e4e7258b810ff07
Parents: bd40cd0
Author: Lyor Goldstein <lg...@apache.org>
Authored: Sun Nov 18 11:32:27 2018 +0200
Committer: Lyor Goldstein <lg...@apache.org>
Committed: Sun Nov 18 11:32:53 2018 +0200
----------------------------------------------------------------------
.../apache/sshd/cli/client/SftpCommandMain.java | 32 +++++++++++++++-----
1 file changed, 25 insertions(+), 7 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/e88a0832/sshd-cli/src/main/java/org/apache/sshd/cli/client/SftpCommandMain.java
----------------------------------------------------------------------
diff --git a/sshd-cli/src/main/java/org/apache/sshd/cli/client/SftpCommandMain.java b/sshd-cli/src/main/java/org/apache/sshd/cli/client/SftpCommandMain.java
index 694d937..b147267 100644
--- a/sshd-cli/src/main/java/org/apache/sshd/cli/client/SftpCommandMain.java
+++ b/sshd-cli/src/main/java/org/apache/sshd/cli/client/SftpCommandMain.java
@@ -39,6 +39,7 @@ import java.util.Map;
import java.util.Objects;
import java.util.ServiceLoader;
import java.util.TreeMap;
+import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import org.apache.sshd.cli.client.helper.SftpFileTransferProgressOutputStream;
@@ -881,7 +882,9 @@ public class SftpCommandMain extends SshClientCliSupport implements Channel {
super();
}
- protected void createDirectories(SftpClient sftp, String remotePath) throws IOException {
+ protected void createDirectories(
+ SftpClient sftp, String remotePath, PrintStream stdout, boolean verbose)
+ throws IOException {
try {
Attributes attrs = sftp.stat(remotePath);
ValidateUtils.checkTrue(attrs.isDirectory(), "Remote path already exists but is not a directory: %s", remotePath);
@@ -893,7 +896,7 @@ public class SftpCommandMain extends SshClientCliSupport implements Channel {
int pos = remotePath.lastIndexOf('/');
ValidateUtils.checkTrue(pos > 0, "No more parents for %s", remotePath);
- createDirectories(sftp, remotePath.substring(0, pos));
+ createDirectories(sftp, remotePath.substring(0, pos), stdout, verbose);
}
protected void transferFile(
@@ -903,13 +906,27 @@ public class SftpCommandMain extends SshClientCliSupport implements Channel {
if (upload) {
int pos = remotePath.lastIndexOf('/');
ValidateUtils.checkTrue(pos > 0, "Missing full remote file path: %s", remotePath);
- createDirectories(sftp, remotePath.substring(0, pos));
+ createDirectories(sftp, remotePath.substring(0, pos), stdout, verbose);
+
+ if (verbose) {
+ stdout.append(" Uploading ").append(Long.toString(Files.size(localPath)))
+ .append(" bytes from ").append(localPath.toString())
+ .append(" to ").println(remotePath);
+ }
} else {
Files.createDirectories(localPath.getParent());
+
+ if (verbose) {
+ Attributes attrs = sftp.stat(remotePath);
+ stdout.append(" Downloading ").append(Long.toString(attrs.getSize()))
+ .append(" bytes from ").append(remotePath)
+ .append(" to ").println(localPath);
+ }
}
boolean withProgress = isShowProgress();
long copySize;
+ long startTime = System.currentTimeMillis();
try (InputStream input = upload ? Files.newInputStream(localPath) : sftp.read(remotePath);
OutputStream target = upload ? sftp.write(remotePath) : Files.newOutputStream(localPath);
OutputStream output = withProgress ? new SftpFileTransferProgressOutputStream(target, stdout) : target) {
@@ -925,9 +942,9 @@ public class SftpCommandMain extends SshClientCliSupport implements Channel {
}
if (verbose) {
- stdout.append(" ")
- .append("Copied ").append(Long.toString(copySize)).append(" bytes")
- .append(" from ").append(upload ? localPath.toString() : remotePath)
+ stdout.append(" Copied ").append(Long.toString(copySize)).append(" bytes")
+ .append(" in ").append(Long.toString(TimeUnit.MILLISECONDS.toSeconds(System.currentTimeMillis() - startTime)))
+ .append(" seconds from ").append(upload ? localPath.toString() : remotePath)
.append(" to ").println(upload ? remotePath : localPath.toString());
}
}
@@ -959,7 +976,8 @@ public class SftpCommandMain extends SshClientCliSupport implements Channel {
if (Files.isDirectory(localPath)) {
try (DirectoryStream<Path> ds = Files.newDirectoryStream(localPath)) {
for (Path entry : ds) {
- String name = entry.getFileName().toString();
+ Path fileName = entry.getFileName();
+ String name = fileName.toString();
transferLocalDir(sftp, localPath.resolve(name), remotePath + "/" + name, stdout, verbose);
}
}
[4/5] mina-sshd git commit: Added SftpCommandMain upload/download
marker progress indication (default=true)
Posted by lg...@apache.org.
Added SftpCommandMain upload/download marker progress indication (default=true)
Project: http://git-wip-us.apache.org/repos/asf/mina-sshd/repo
Commit: http://git-wip-us.apache.org/repos/asf/mina-sshd/commit/bd40cd0c
Tree: http://git-wip-us.apache.org/repos/asf/mina-sshd/tree/bd40cd0c
Diff: http://git-wip-us.apache.org/repos/asf/mina-sshd/diff/bd40cd0c
Branch: refs/heads/master
Commit: bd40cd0c70866ec4543b61cbf87c57296296019b
Parents: 1a8924d
Author: Lyor Goldstein <lg...@apache.org>
Authored: Sun Nov 18 11:12:28 2018 +0200
Committer: Lyor Goldstein <lg...@apache.org>
Committed: Sun Nov 18 11:32:53 2018 +0200
----------------------------------------------------------------------
CHANGES.md | 14 ++
.../apache/sshd/cli/client/SftpCommandMain.java | 201 ++++++++++++++-----
.../SftpFileTransferProgressOutputStream.java | 129 ++++++++++++
3 files changed, 297 insertions(+), 47 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/bd40cd0c/CHANGES.md
----------------------------------------------------------------------
diff --git a/CHANGES.md b/CHANGES.md
index e79d215..60f941b 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -88,3 +88,17 @@ in order to provide key file(s) location information
* [SSHD-866](https://issues.apache.org/jira/browse/SSHD-866) - Counting empty challenges separately when enforcing
max. attempts during `keyboard-interactive` authentication
+
+* `SftpCommandMain` shows by default `get/put` command progress using the hash sign (`#`) marker. The marker
+can be enabled/disabled via the `progress` command:
+
+```
+ > progress
+
+ ... reponse is whether it is 'on' or 'off'
+
+ > progress on/off
+
+ ... set the progress marker indicator ...
+
+```
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/bd40cd0c/sshd-cli/src/main/java/org/apache/sshd/cli/client/SftpCommandMain.java
----------------------------------------------------------------------
diff --git a/sshd-cli/src/main/java/org/apache/sshd/cli/client/SftpCommandMain.java b/sshd-cli/src/main/java/org/apache/sshd/cli/client/SftpCommandMain.java
index 99427ff..694d937 100644
--- a/sshd-cli/src/main/java/org/apache/sshd/cli/client/SftpCommandMain.java
+++ b/sshd-cli/src/main/java/org/apache/sshd/cli/client/SftpCommandMain.java
@@ -41,6 +41,7 @@ import java.util.ServiceLoader;
import java.util.TreeMap;
import java.util.logging.Level;
+import org.apache.sshd.cli.client.helper.SftpFileTransferProgressOutputStream;
import org.apache.sshd.client.ClientFactoryManager;
import org.apache.sshd.client.session.ClientSession;
import org.apache.sshd.client.subsystem.sftp.SftpClient;
@@ -89,6 +90,7 @@ public class SftpCommandMain extends SshClientCliSupport implements Channel {
private final Map<String, SftpCommandExecutor> commandsMap;
private String cwdRemote;
private String cwdLocal;
+ private boolean showProgress = true;
public SftpCommandMain(SftpClient client) {
this.client = Objects.requireNonNull(client, "No client");
@@ -113,6 +115,7 @@ public class SftpCommandMain extends SshClientCliSupport implements Channel {
new StatVfsCommandExecutor(),
new GetCommandExecutor(),
new PutCommandExecutor(),
+ new ProgressCommandExecutor(),
new HelpCommandExecutor()
)) {
String name = e.getName();
@@ -205,9 +208,11 @@ public class SftpCommandMain extends SshClientCliSupport implements Channel {
}
}
- protected <A extends Appendable> A appendFileAttributes(A stdout, SftpClient sftp, String path, Attributes attrs) throws IOException {
- stdout.append('\t').append(Long.toString(attrs.getSize()))
- .append('\t').append(SftpFileSystemProvider.getRWXPermissions(attrs.getPermissions()));
+ protected <A extends Appendable> A appendFileAttributes(
+ A stdout, SftpClient sftp, String path, Attributes attrs)
+ throws IOException {
+ stdout.append(" ").append(Long.toString(attrs.getSize()))
+ .append(" ").append(SftpFileSystemProvider.getRWXPermissions(attrs.getPermissions()));
if (attrs.isSymbolicLink()) {
String linkValue = sftp.readLink(path);
stdout.append(" => ")
@@ -234,6 +239,14 @@ public class SftpCommandMain extends SshClientCliSupport implements Channel {
cwdLocal = path;
}
+ public boolean isShowProgress() {
+ return showProgress;
+ }
+
+ public void setShowProgress(boolean showProgress) {
+ this.showProgress = showProgress;
+ }
+
@Override
public boolean isOpen() {
return client.isOpen();
@@ -249,7 +262,7 @@ public class SftpCommandMain extends SshClientCliSupport implements Channel {
//////////////////////////////////////////////////////////////////////////
public static <A extends Appendable> A appendInfoValue(A sb, CharSequence name, Object value) throws IOException {
- sb.append('\t').append(name).append(": ").append(Objects.toString(value));
+ sb.append(" ").append(name).append(": ").append(Objects.toString(value));
return sb;
}
@@ -309,7 +322,9 @@ public class SftpCommandMain extends SshClientCliSupport implements Channel {
setupLogging(level, stdout, stderr, logStream);
}
- ClientSession session = (logStream == null) ? null : setupClientSession(SFTP_PORT_OPTION, stdin, stdout, stderr, args);
+ ClientSession session = (logStream == null)
+ ? null
+ : setupClientSession(SFTP_PORT_OPTION, stdin, stdout, stderr, args);
if (session == null) {
System.err.println("usage: sftp [-v[v][v]] [-E logoutput] [-i identity] [-io nio2|mina|netty]"
+ " [-l login] [" + SFTP_PORT_OPTION + " port] [-o option=value]"
@@ -347,7 +362,9 @@ public class SftpCommandMain extends SshClientCliSupport implements Channel {
}
@Override
- public boolean executeCommand(String args, BufferedReader stdin, PrintStream stdout, PrintStream stderr) throws Exception {
+ public boolean executeCommand(
+ String args, BufferedReader stdin, PrintStream stdout, PrintStream stderr)
+ throws Exception {
ValidateUtils.checkTrue(GenericUtils.isEmpty(args), "Unexpected arguments: %s", args);
stdout.println("Exiting");
return true;
@@ -365,10 +382,12 @@ public class SftpCommandMain extends SshClientCliSupport implements Channel {
}
@Override
- public boolean executeCommand(String args, BufferedReader stdin, PrintStream stdout, PrintStream stderr) throws Exception {
+ public boolean executeCommand(
+ String args, BufferedReader stdin, PrintStream stdout, PrintStream stderr)
+ throws Exception {
ValidateUtils.checkTrue(GenericUtils.isEmpty(args), "Unexpected arguments: %s", args);
- stdout.append('\t').append("Remote: ").println(getCurrentRemoteDirectory());
- stdout.append('\t').append("Local: ").println(getCurrentLocalDirectory());
+ stdout.append(" ").append("Remote: ").println(getCurrentRemoteDirectory());
+ stdout.append(" ").append("Local: ").println(getCurrentLocalDirectory());
return false;
}
}
@@ -384,7 +403,9 @@ public class SftpCommandMain extends SshClientCliSupport implements Channel {
}
@Override
- public boolean executeCommand(String args, BufferedReader stdin, PrintStream stdout, PrintStream stderr) throws Exception {
+ public boolean executeCommand(
+ String args, BufferedReader stdin, PrintStream stdout, PrintStream stderr)
+ throws Exception {
ValidateUtils.checkTrue(GenericUtils.isEmpty(args), "Unexpected arguments: %s", args);
SftpClient sftp = getClient();
ClientSession session = sftp.getSession();
@@ -414,7 +435,9 @@ public class SftpCommandMain extends SshClientCliSupport implements Channel {
}
@Override
- public boolean executeCommand(String args, BufferedReader stdin, PrintStream stdout, PrintStream stderr) throws Exception {
+ public boolean executeCommand(
+ String args, BufferedReader stdin, PrintStream stdout, PrintStream stderr)
+ throws Exception {
ValidateUtils.checkTrue(GenericUtils.isEmpty(args), "Unexpected arguments: %s", args);
SftpClient sftp = getClient();
ClientSession session = sftp.getSession();
@@ -442,11 +465,13 @@ public class SftpCommandMain extends SshClientCliSupport implements Channel {
}
@Override
- public boolean executeCommand(String args, BufferedReader stdin, PrintStream stdout, PrintStream stderr) throws Exception {
+ public boolean executeCommand(
+ String args, BufferedReader stdin, PrintStream stdout, PrintStream stderr)
+ throws Exception {
ValidateUtils.checkTrue(GenericUtils.isEmpty(args), "Unexpected arguments: %s", args);
SftpClient sftp = getClient();
Session session = sftp.getSession();
- stdout.append('\t').println(session.getServerVersion());
+ stdout.append(" ").println(session.getServerVersion());
Map<String, byte[]> extensions = sftp.getServerExtensions();
Map<String, ?> parsed = ParserUtils.parse(extensions);
@@ -457,7 +482,7 @@ public class SftpCommandMain extends SshClientCliSupport implements Channel {
extensions.forEach((name, value) -> {
Object info = parsed.get(name);
- stdout.append('\t').append(name).append(": ");
+ stdout.append(" ").append(name).append(": ");
if (info == null) {
stdout.println(BufferUtils.toHex(value));
} else {
@@ -480,10 +505,12 @@ public class SftpCommandMain extends SshClientCliSupport implements Channel {
}
@Override
- public boolean executeCommand(String args, BufferedReader stdin, PrintStream stdout, PrintStream stderr) throws Exception {
+ public boolean executeCommand(
+ String args, BufferedReader stdin, PrintStream stdout, PrintStream stderr)
+ throws Exception {
ValidateUtils.checkTrue(GenericUtils.isEmpty(args), "Unexpected arguments: %s", args);
SftpClient sftp = getClient();
- stdout.append('\t').println(sftp.getVersion());
+ stdout.append(" ").println(sftp.getVersion());
return false;
}
}
@@ -499,7 +526,9 @@ public class SftpCommandMain extends SshClientCliSupport implements Channel {
}
@Override
- public boolean executeCommand(String args, BufferedReader stdin, PrintStream stdout, PrintStream stderr) throws Exception {
+ public boolean executeCommand(
+ String args, BufferedReader stdin, PrintStream stdout, PrintStream stderr)
+ throws Exception {
ValidateUtils.checkNotNullAndNotEmpty(args, "No remote directory specified");
String newPath = resolveRemotePath(args);
@@ -520,7 +549,9 @@ public class SftpCommandMain extends SshClientCliSupport implements Channel {
}
@Override
- public boolean executeCommand(String args, BufferedReader stdin, PrintStream stdout, PrintStream stderr) throws Exception {
+ public boolean executeCommand(
+ String args, BufferedReader stdin, PrintStream stdout, PrintStream stderr)
+ throws Exception {
if (GenericUtils.isEmpty(args)) {
setCurrentLocalDirectory(System.getProperty("user.home"));
} else {
@@ -545,7 +576,9 @@ public class SftpCommandMain extends SshClientCliSupport implements Channel {
}
@Override
- public boolean executeCommand(String args, BufferedReader stdin, PrintStream stdout, PrintStream stderr) throws Exception {
+ public boolean executeCommand(
+ String args, BufferedReader stdin, PrintStream stdout, PrintStream stderr)
+ throws Exception {
ValidateUtils.checkNotNullAndNotEmpty(args, "No remote directory specified");
String path = resolveRemotePath(args);
@@ -566,7 +599,9 @@ public class SftpCommandMain extends SshClientCliSupport implements Channel {
}
@Override
- public boolean executeCommand(String args, BufferedReader stdin, PrintStream stdout, PrintStream stderr) throws Exception {
+ public boolean executeCommand(
+ String args, BufferedReader stdin, PrintStream stdout, PrintStream stderr)
+ throws Exception {
String[] comps = GenericUtils.split(args, ' ');
int numComps = GenericUtils.length(comps);
String pathArg = (numComps <= 0) ? null : GenericUtils.trimToEmpty(comps[numComps - 1]);
@@ -584,7 +619,7 @@ public class SftpCommandMain extends SshClientCliSupport implements Channel {
for (SftpClient.DirEntry entry : sftp.readDir(path)) {
String fileName = entry.getFilename();
SftpClient.Attributes attrs = entry.getAttributes();
- appendFileAttributes(stdout.append('\t').append(fileName), sftp, path + "/" + fileName, attrs).println();
+ appendFileAttributes(stdout.append(" ").append(fileName), sftp, path + "/" + fileName, attrs).println();
if (showLongName) {
stdout.append("\t\tlong-name: ").println(entry.getLongFilename());
}
@@ -605,7 +640,9 @@ public class SftpCommandMain extends SshClientCliSupport implements Channel {
}
@Override
- public boolean executeCommand(String args, BufferedReader stdin, PrintStream stdout, PrintStream stderr) throws Exception {
+ public boolean executeCommand(
+ String args, BufferedReader stdin, PrintStream stdout, PrintStream stderr)
+ throws Exception {
String[] comps = GenericUtils.split(args, ' ');
int numArgs = GenericUtils.length(comps);
ValidateUtils.checkTrue(numArgs >= 1, "No arguments");
@@ -643,14 +680,16 @@ public class SftpCommandMain extends SshClientCliSupport implements Channel {
} else {
sftp.remove(path);
if (verbose) {
- stdout.append('\t').append("Removed ").println(path);
+ stdout.append(" ").append("Removed ").println(path);
}
}
return false;
}
- private void removeRecursive(SftpClient sftp, String path, Attributes attrs, PrintStream stdout, boolean verbose) throws IOException {
+ private void removeRecursive(
+ SftpClient sftp, String path, Attributes attrs, PrintStream stdout, boolean verbose)
+ throws IOException {
if (attrs.isDirectory()) {
for (DirEntry entry : sftp.readDir(path)) {
String name = entry.getFilename();
@@ -666,13 +705,13 @@ public class SftpCommandMain extends SshClientCliSupport implements Channel {
sftp.remove(path);
} else {
if (verbose) {
- stdout.append('\t').append("Skip special file ").println(path);
+ stdout.append(" ").append("Skip special file ").println(path);
return;
}
}
if (verbose) {
- stdout.append('\t').append("Removed ").println(path);
+ stdout.append(" ").append("Removed ").println(path);
}
}
}
@@ -688,7 +727,9 @@ public class SftpCommandMain extends SshClientCliSupport implements Channel {
}
@Override
- public boolean executeCommand(String args, BufferedReader stdin, PrintStream stdout, PrintStream stderr) throws Exception {
+ public boolean executeCommand(
+ String args, BufferedReader stdin, PrintStream stdout, PrintStream stderr)
+ throws Exception {
ValidateUtils.checkNotNullAndNotEmpty(args, "No remote directory specified");
String path = resolveRemotePath(args);
@@ -709,7 +750,9 @@ public class SftpCommandMain extends SshClientCliSupport implements Channel {
}
@Override
- public boolean executeCommand(String args, BufferedReader stdin, PrintStream stdout, PrintStream stderr) throws Exception {
+ public boolean executeCommand(
+ String args, BufferedReader stdin, PrintStream stdout, PrintStream stderr)
+ throws Exception {
String[] comps = GenericUtils.split(args, ' ');
ValidateUtils.checkTrue(GenericUtils.length(comps) == 2, "Invalid number of arguments: %s", args);
@@ -732,7 +775,9 @@ public class SftpCommandMain extends SshClientCliSupport implements Channel {
}
@Override
- public boolean executeCommand(String args, BufferedReader stdin, PrintStream stdout, PrintStream stderr) throws Exception {
+ public boolean executeCommand(
+ String args, BufferedReader stdin, PrintStream stdout, PrintStream stderr)
+ throws Exception {
String[] comps = GenericUtils.split(args, ' ');
int numArgs = GenericUtils.length(comps);
ValidateUtils.checkTrue(numArgs <= 1, "Invalid number of arguments: %s", args);
@@ -741,7 +786,8 @@ public class SftpCommandMain extends SshClientCliSupport implements Channel {
OpenSSHStatPathExtension ext = sftp.getExtension(OpenSSHStatPathExtension.class);
ValidateUtils.checkTrue(ext.isSupported(), "Extension not supported by server: %s", ext.getName());
- String remPath = resolveRemotePath((numArgs >= 1) ? GenericUtils.trimToEmpty(comps[0]) : GenericUtils.trimToEmpty(args));
+ String remPath = resolveRemotePath(
+ (numArgs >= 1) ? GenericUtils.trimToEmpty(comps[0]) : GenericUtils.trimToEmpty(args));
OpenSSHStatExtensionInfo info = ext.stat(remPath);
Field[] fields = info.getClass().getFields();
for (Field f : fields) {
@@ -752,7 +798,7 @@ public class SftpCommandMain extends SshClientCliSupport implements Channel {
}
Object value = f.get(info);
- stdout.append('\t').append(name).append(": ").println(value);
+ stdout.append(" ").append(name).append(": ").println(value);
}
return false;
@@ -770,7 +816,9 @@ public class SftpCommandMain extends SshClientCliSupport implements Channel {
}
@Override
- public boolean executeCommand(String args, BufferedReader stdin, PrintStream stdout, PrintStream stderr) throws Exception {
+ public boolean executeCommand(
+ String args, BufferedReader stdin, PrintStream stdout, PrintStream stderr)
+ throws Exception {
String[] comps = GenericUtils.split(args, ' ');
ValidateUtils.checkTrue(GenericUtils.length(comps) <= 1, "Invalid number of arguments: %s", args);
@@ -800,7 +848,7 @@ public class SftpCommandMain extends SshClientCliSupport implements Channel {
String path = GenericUtils.trimToEmpty(resolveRemotePath(args));
SftpClient client = getClient();
String linkData = client.readLink(path);
- stdout.append('\t').println(linkData);
+ stdout.append(" ").println(linkData);
return false;
}
}
@@ -817,10 +865,12 @@ public class SftpCommandMain extends SshClientCliSupport implements Channel {
@Override
@SuppressWarnings("synthetic-access")
- public boolean executeCommand(String args, BufferedReader stdin, PrintStream stdout, PrintStream stderr) throws Exception {
+ public boolean executeCommand(
+ String args, BufferedReader stdin, PrintStream stdout, PrintStream stderr)
+ throws Exception {
ValidateUtils.checkTrue(GenericUtils.isEmpty(args), "Unexpected arguments: %s", args);
for (String cmd : commandsMap.keySet()) {
- stdout.append('\t').println(cmd);
+ stdout.append(" ").println(cmd);
}
return false;
}
@@ -846,7 +896,9 @@ public class SftpCommandMain extends SshClientCliSupport implements Channel {
createDirectories(sftp, remotePath.substring(0, pos));
}
- protected void transferFile(SftpClient sftp, Path localPath, String remotePath, boolean upload, PrintStream stdout, boolean verbose) throws IOException {
+ protected void transferFile(
+ SftpClient sftp, Path localPath, String remotePath, boolean upload, PrintStream stdout, boolean verbose)
+ throws IOException {
// Create the file's hierarchy
if (upload) {
int pos = remotePath.lastIndexOf('/');
@@ -856,19 +908,33 @@ public class SftpCommandMain extends SshClientCliSupport implements Channel {
Files.createDirectories(localPath.getParent());
}
+ boolean withProgress = isShowProgress();
+ long copySize;
try (InputStream input = upload ? Files.newInputStream(localPath) : sftp.read(remotePath);
- OutputStream output = upload ? sftp.write(remotePath) : Files.newOutputStream(localPath)) {
- IoUtils.copy(input, output, SftpClient.IO_BUFFER_SIZE);
+ OutputStream target = upload ? sftp.write(remotePath) : Files.newOutputStream(localPath);
+ OutputStream output = withProgress ? new SftpFileTransferProgressOutputStream(target, stdout) : target) {
+ if (withProgress) {
+ stdout.println();
+ }
+
+ copySize = IoUtils.copy(input, output, SftpClient.IO_BUFFER_SIZE);
+
+ if (withProgress) {
+ stdout.println();
+ }
}
if (verbose) {
- stdout.append('\t')
- .append("Copied ").append(upload ? localPath.toString() : remotePath)
- .append(" to ").println(upload ? remotePath : localPath.toString());
+ stdout.append(" ")
+ .append("Copied ").append(Long.toString(copySize)).append(" bytes")
+ .append(" from ").append(upload ? localPath.toString() : remotePath)
+ .append(" to ").println(upload ? remotePath : localPath.toString());
}
}
- protected void transferRemoteDir(SftpClient sftp, Path localPath, String remotePath, Attributes attrs, PrintStream stdout, boolean verbose) throws IOException {
+ protected void transferRemoteDir(
+ SftpClient sftp, Path localPath, String remotePath, Attributes attrs, PrintStream stdout, boolean verbose)
+ throws IOException {
if (attrs.isDirectory()) {
for (DirEntry entry : sftp.readDir(remotePath)) {
String name = entry.getFilename();
@@ -882,12 +948,14 @@ public class SftpCommandMain extends SshClientCliSupport implements Channel {
transferFile(sftp, localPath, remotePath, false, stdout, verbose);
} else {
if (verbose) {
- stdout.append('\t').append("Skip remote special file ").println(remotePath);
+ stdout.append(" ").append("Skip remote special file ").println(remotePath);
}
}
}
- protected void transferLocalDir(SftpClient sftp, Path localPath, String remotePath, PrintStream stdout, boolean verbose) throws IOException {
+ protected void transferLocalDir(
+ SftpClient sftp, Path localPath, String remotePath, PrintStream stdout, boolean verbose)
+ throws IOException {
if (Files.isDirectory(localPath)) {
try (DirectoryStream<Path> ds = Files.newDirectoryStream(localPath)) {
for (Path entry : ds) {
@@ -899,7 +967,7 @@ public class SftpCommandMain extends SshClientCliSupport implements Channel {
transferFile(sftp, localPath, remotePath, true, stdout, verbose);
} else {
if (verbose) {
- stdout.append('\t').append("Skip local special file ").println(localPath);
+ stdout.append(" ").append("Skip local special file ").println(localPath);
}
}
}
@@ -980,7 +1048,9 @@ public class SftpCommandMain extends SshClientCliSupport implements Channel {
}
@Override
- public boolean executeCommand(String args, BufferedReader stdin, PrintStream stdout, PrintStream stderr) throws Exception {
+ public boolean executeCommand(
+ String args, BufferedReader stdin, PrintStream stdout, PrintStream stderr)
+ throws Exception {
executeCommand(args, false, stdout);
return false;
}
@@ -997,9 +1067,46 @@ public class SftpCommandMain extends SshClientCliSupport implements Channel {
}
@Override
- public boolean executeCommand(String args, BufferedReader stdin, PrintStream stdout, PrintStream stderr) throws Exception {
+ public boolean executeCommand(
+ String args, BufferedReader stdin, PrintStream stdout, PrintStream stderr)
+ throws Exception {
executeCommand(args, true, stdout);
return false;
}
}
+
+ private class ProgressCommandExecutor implements SftpCommandExecutor {
+ ProgressCommandExecutor() {
+ super();
+ }
+
+ @Override
+ public String getName() {
+ return "progress";
+ }
+
+ @Override
+ public boolean executeCommand(
+ String args, BufferedReader stdin, PrintStream stdout, PrintStream stderr)
+ throws Exception {
+ String[] comps = GenericUtils.split(args, ' ');
+ int numArgs = GenericUtils.length(comps);
+ if (numArgs <= 0) {
+ stdout.append(" ").append(getName()).append(' ').println(isShowProgress() ? "on" : "off");
+ } else {
+ ValidateUtils.checkTrue(numArgs == 1, "Invalid arguments count: %d", numArgs);
+
+ String argVal = comps[0];
+ if ("on".equalsIgnoreCase(argVal)) {
+ setShowProgress(true);
+ } else if ("off".equalsIgnoreCase(argVal)) {
+ setShowProgress(false);
+ } else {
+ throw new IllegalArgumentException("Unknown value: " + argVal);
+ }
+ }
+
+ return false;
+ }
+ }
}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/bd40cd0c/sshd-cli/src/main/java/org/apache/sshd/cli/client/helper/SftpFileTransferProgressOutputStream.java
----------------------------------------------------------------------
diff --git a/sshd-cli/src/main/java/org/apache/sshd/cli/client/helper/SftpFileTransferProgressOutputStream.java b/sshd-cli/src/main/java/org/apache/sshd/cli/client/helper/SftpFileTransferProgressOutputStream.java
new file mode 100644
index 0000000..26330bc
--- /dev/null
+++ b/sshd-cli/src/main/java/org/apache/sshd/cli/client/helper/SftpFileTransferProgressOutputStream.java
@@ -0,0 +1,129 @@
+/*
+ * 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.cli.client.helper;
+
+import java.io.FilterOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.StreamCorruptedException;
+import java.util.Objects;
+
+import org.apache.sshd.common.util.ValidateUtils;
+import org.apache.sshd.common.util.io.IoUtils;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class SftpFileTransferProgressOutputStream extends FilterOutputStream {
+ public static final char DEFAULT_PROGRESS_CHAR = '#';
+ public static final int DEFAULT_MARKS_PER_LINE = 72;
+ public static final int DEFAULT_MARKER_SIZE = IoUtils.DEFAULT_COPY_SIZE;
+
+ private final int markerSize;
+ private final char markerChar;
+ private final int markersPerLine;
+ private final Appendable stdout;
+ private final byte[] workBuf = {0};
+ private long byteCount;
+ private long lastMarkOffset;
+ private int curMarkersInLine;
+
+ public SftpFileTransferProgressOutputStream(OutputStream out, Appendable stdout) {
+ this(out, DEFAULT_MARKER_SIZE, DEFAULT_PROGRESS_CHAR, DEFAULT_MARKS_PER_LINE, stdout);
+ }
+
+ public SftpFileTransferProgressOutputStream(
+ OutputStream out, int markerSize, char markerChar, int markersPerLine, Appendable stdout) {
+ super(Objects.requireNonNull(out, "No target stream"));
+
+ ValidateUtils.checkTrue(markerSize > 0, "Invalid marker size: %d", markerSize);
+ this.markerSize = markerSize;
+
+ if ((markerChar <= ' ') || (markerChar > 0x7E)) {
+ throw new IllegalArgumentException("Non-printable marker character: 0x" + Integer.toHexString(markerChar));
+ }
+ this.markerChar = markerChar;
+
+ ValidateUtils.checkTrue(markersPerLine > 0, "Invalid markers per line: %d", markersPerLine);
+
+ this.markersPerLine = markersPerLine;
+ this.stdout = Objects.requireNonNull(stdout, "No progress report target");
+ }
+
+ public int getMarkerSize() {
+ return markerSize;
+ }
+
+ public char getMarkerChar() {
+ return markerChar;
+ }
+
+ public int getMarkersPerLine() {
+ return markersPerLine;
+ }
+
+ public Appendable getStdout() {
+ return stdout;
+ }
+
+ @Override
+ public void write(int b) throws IOException {
+ workBuf[0] = (byte) (b & 0xFF);
+ write(workBuf, 0, 1);
+ }
+
+ @Override
+ public void write(byte[] b) throws IOException {
+ write(b, 0, b.length);
+ }
+
+ @Override
+ public void write(byte[] b, int off, int len) throws IOException {
+ if ((len < 0) || (off < 0)) {
+ throw new StreamCorruptedException("Invalid offset (" + off + ")/length(" + len + ")");
+ }
+ this.out.write(b, off, len);
+
+ byteCount += len;
+
+ long reportDiff = byteCount - lastMarkOffset;
+ int reportSize = getMarkerSize();
+ long markersCount = reportDiff / reportSize;
+ appendMarkers((int) markersCount);
+ lastMarkOffset += markersCount * reportSize;
+ }
+
+ protected void appendMarkers(int markersCount) throws IOException {
+ if (markersCount <= 0) {
+ return;
+ }
+
+ Appendable target = getStdout();
+ char marker = getMarkerChar();
+ for (int index = 1, limit = getMarkersPerLine(); index <= markersCount; index++) {
+ target.append(marker);
+ curMarkersInLine++;
+ if (curMarkersInLine >= limit) {
+ target.append(System.lineSeparator());
+ curMarkersInLine = 0;
+ }
+ }
+ }
+}
[5/5] mina-sshd git commit: [SSHD-868] Validate SFTP response packet
buffer
Posted by lg...@apache.org.
[SSHD-868] Validate SFTP response packet buffer
Project: http://git-wip-us.apache.org/repos/asf/mina-sshd/repo
Commit: http://git-wip-us.apache.org/repos/asf/mina-sshd/commit/775f3495
Tree: http://git-wip-us.apache.org/repos/asf/mina-sshd/tree/775f3495
Diff: http://git-wip-us.apache.org/repos/asf/mina-sshd/diff/775f3495
Branch: refs/heads/master
Commit: 775f34955151d6ec241a1ad3a634c53a87386a64
Parents: db6e5b5
Author: Lyor Goldstein <lg...@apache.org>
Authored: Sun Nov 18 09:12:40 2018 +0200
Committer: Lyor Goldstein <lg...@apache.org>
Committed: Sun Nov 18 11:32:53 2018 +0200
----------------------------------------------------------------------
.../helpers/AbstractSftpClientExtension.java | 13 +++++++++
.../subsystem/sftp/impl/AbstractSftpClient.java | 23 ++++++++++++++++
.../sshd/client/subsystem/sftp/ClientTest.java | 29 ++++++++++++++------
3 files changed, 56 insertions(+), 9 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/775f3495/sshd-sftp/src/main/java/org/apache/sshd/client/subsystem/sftp/extensions/helpers/AbstractSftpClientExtension.java
----------------------------------------------------------------------
diff --git a/sshd-sftp/src/main/java/org/apache/sshd/client/subsystem/sftp/extensions/helpers/AbstractSftpClientExtension.java b/sshd-sftp/src/main/java/org/apache/sshd/client/subsystem/sftp/extensions/helpers/AbstractSftpClientExtension.java
index 6e1f52b..dc9379a 100644
--- a/sshd-sftp/src/main/java/org/apache/sshd/client/subsystem/sftp/extensions/helpers/AbstractSftpClientExtension.java
+++ b/sshd-sftp/src/main/java/org/apache/sshd/client/subsystem/sftp/extensions/helpers/AbstractSftpClientExtension.java
@@ -175,6 +175,8 @@ public abstract class AbstractSftpClientExtension extends AbstractLoggingBean im
int length = buffer.getInt();
int type = buffer.getUByte();
int id = buffer.getInt();
+ validateIncomingResponse(SftpConstants.SSH_FXP_EXTENDED, id, type, length, buffer);
+
if (type == SftpConstants.SSH_FXP_STATUS) {
int substatus = buffer.getInt();
String msg = buffer.getString();
@@ -196,6 +198,17 @@ public abstract class AbstractSftpClientExtension extends AbstractLoggingBean im
}
}
+ protected void validateIncomingResponse(
+ int cmd, int id, int type, int length, Buffer buffer)
+ throws IOException {
+ int remaining = buffer.available();
+ if ((length < 0) || (length > (remaining + 5 /* type + id */))) {
+ throw new SshException("Bad length (" + length + ") for remaining data (" + remaining + ")"
+ + " in response to " + SftpConstants.getCommandMessageName(cmd)
+ + ": type=" + SftpConstants.getCommandMessageName(type) + ", id=" + id);
+ }
+ }
+
protected void throwStatusException(int id, int substatus, String msg, String lang) throws IOException {
throw new SftpException(substatus, msg);
}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/775f3495/sshd-sftp/src/main/java/org/apache/sshd/client/subsystem/sftp/impl/AbstractSftpClient.java
----------------------------------------------------------------------
diff --git a/sshd-sftp/src/main/java/org/apache/sshd/client/subsystem/sftp/impl/AbstractSftpClient.java b/sshd-sftp/src/main/java/org/apache/sshd/client/subsystem/sftp/impl/AbstractSftpClient.java
index 3d607f3..d7b45a8 100644
--- a/sshd-sftp/src/main/java/org/apache/sshd/client/subsystem/sftp/impl/AbstractSftpClient.java
+++ b/sshd-sftp/src/main/java/org/apache/sshd/client/subsystem/sftp/impl/AbstractSftpClient.java
@@ -174,6 +174,8 @@ public abstract class AbstractSftpClient extends AbstractSubsystemClient impleme
int length = buffer.getInt();
int type = buffer.getUByte();
int id = buffer.getInt();
+ validateIncomingResponse(cmd, id, type, length, buffer);
+
if (type == SftpConstants.SSH_FXP_STATUS) {
int substatus = buffer.getInt();
String msg = buffer.getString();
@@ -229,6 +231,8 @@ public abstract class AbstractSftpClient extends AbstractSubsystemClient impleme
int length = buffer.getInt();
int type = buffer.getUByte();
int id = buffer.getInt();
+ validateIncomingResponse(cmd, id, type, length, buffer);
+
if (type == SftpConstants.SSH_FXP_HANDLE) {
return ValidateUtils.checkNotNullAndNotEmpty(buffer.getBytes(), "Null/empty handle in buffer", GenericUtils.EMPTY_OBJECT_ARRAY);
}
@@ -273,6 +277,8 @@ public abstract class AbstractSftpClient extends AbstractSubsystemClient impleme
int length = buffer.getInt();
int type = buffer.getUByte();
int id = buffer.getInt();
+ validateIncomingResponse(cmd, id, type, length, buffer);
+
if (type == SftpConstants.SSH_FXP_ATTRS) {
return readAttributes(cmd, buffer, new AtomicInteger(0));
}
@@ -320,6 +326,8 @@ public abstract class AbstractSftpClient extends AbstractSubsystemClient impleme
int length = buffer.getInt();
int type = buffer.getUByte();
int id = buffer.getInt();
+ validateIncomingResponse(cmd, id, type, length, buffer);
+
if (type == SftpConstants.SSH_FXP_NAME) {
int len = buffer.getInt();
if (len != 1) {
@@ -792,6 +800,8 @@ public abstract class AbstractSftpClient extends AbstractSubsystemClient impleme
int length = buffer.getInt();
int type = buffer.getUByte();
int id = buffer.getInt();
+ validateIncomingResponse(cmd, id, type, length, buffer);
+
if (type == SftpConstants.SSH_FXP_DATA) {
int len = buffer.getInt();
buffer.getRawBytes(dst, dstoff, len);
@@ -947,6 +957,8 @@ public abstract class AbstractSftpClient extends AbstractSubsystemClient impleme
int length = buffer.getInt();
int type = buffer.getUByte();
int id = buffer.getInt();
+ validateIncomingResponse(cmd, id, type, length, buffer);
+
boolean traceEnabled = log.isTraceEnabled();
if (type == SftpConstants.SSH_FXP_NAME) {
ClientChannel channel = getClientChannel();
@@ -1011,6 +1023,17 @@ public abstract class AbstractSftpClient extends AbstractSubsystemClient impleme
return handleUnknownDirListingPacket(cmd, id, type, length, buffer);
}
+ protected void validateIncomingResponse(
+ int cmd, int id, int type, int length, Buffer buffer)
+ throws IOException {
+ int remaining = buffer.available();
+ if ((length < 0) || (length > (remaining + 5 /* type + id */))) {
+ throw new SshException("Bad length (" + length + ") for remaining data (" + remaining + ")"
+ + " in response to " + SftpConstants.getCommandMessageName(cmd)
+ + ": type=" + SftpConstants.getCommandMessageName(type) + ", id=" + id);
+ }
+ }
+
protected List<DirEntry> handleUnknownDirListingPacket(
int cmd, int id, int type, int length, Buffer buffer)
throws IOException {
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/775f3495/sshd-sftp/src/test/java/org/apache/sshd/client/subsystem/sftp/ClientTest.java
----------------------------------------------------------------------
diff --git a/sshd-sftp/src/test/java/org/apache/sshd/client/subsystem/sftp/ClientTest.java b/sshd-sftp/src/test/java/org/apache/sshd/client/subsystem/sftp/ClientTest.java
index 94c492f..d5238b6 100644
--- a/sshd-sftp/src/test/java/org/apache/sshd/client/subsystem/sftp/ClientTest.java
+++ b/sshd-sftp/src/test/java/org/apache/sshd/client/subsystem/sftp/ClientTest.java
@@ -236,7 +236,8 @@ public class ClientTest extends BaseTestSupport {
});
testClientListener(channelHolder, SftpClient.class, () -> {
try {
- return SftpClientFactory.instance().createSftpClient(session);
+ SftpClientFactory clientFactory = SftpClientFactory.instance();
+ return clientFactory.createSftpClient(session);
} catch (IOException e) {
throw new RuntimeException(e);
}
@@ -246,7 +247,9 @@ public class ClientTest extends BaseTestSupport {
}
}
- private <C extends Closeable> void testClientListener(AtomicReference<Channel> channelHolder, Class<C> channelType, Factory<? extends C> factory) throws Exception {
+ private <C extends Closeable> void testClientListener(
+ AtomicReference<Channel> channelHolder, Class<C> channelType, Factory<? extends C> factory)
+ throws Exception {
assertNull(channelType.getSimpleName() + ": Unexpected currently active channel", channelHolder.get());
try (C instance = factory.create()) {
@@ -333,29 +336,35 @@ public class ClientTest extends BaseTestSupport {
assertListenerSizes("ClientStop", clientListeners, 0, 1);
}
- private static void assertListenerSizes(String phase, Map<String, ? extends TestChannelListener> listeners, int activeSize, int openSize) {
+ private static void assertListenerSizes(
+ String phase, Map<String, ? extends TestChannelListener> listeners, int activeSize, int openSize) {
assertListenerSizes(phase, listeners.values(), activeSize, openSize);
}
- private static void assertListenerSizes(String phase, Collection<? extends TestChannelListener> listeners, int activeSize, int openSize) {
+ private static void assertListenerSizes(
+ String phase, Collection<? extends TestChannelListener> listeners, int activeSize, int openSize) {
if (GenericUtils.isEmpty(listeners)) {
return;
}
for (TestChannelListener l : listeners) {
if (activeSize >= 0) {
- assertEquals(phase + ": mismatched active channels size for " + l.getName() + " listener", activeSize, GenericUtils.size(l.getActiveChannels()));
+ assertEquals(phase + ": mismatched active channels size for " + l.getName() + " listener",
+ activeSize, GenericUtils.size(l.getActiveChannels()));
}
if (openSize >= 0) {
- assertEquals(phase + ": mismatched open channels size for " + l.getName() + " listener", openSize, GenericUtils.size(l.getOpenChannels()));
+ assertEquals(phase + ": mismatched open channels size for " + l.getName() + " listener",
+ openSize, GenericUtils.size(l.getOpenChannels()));
}
- assertEquals(phase + ": unexpected failed channels size for " + l.getName() + " listener", 0, GenericUtils.size(l.getFailedChannels()));
+ assertEquals(phase + ": unexpected failed channels size for " + l.getName() + " listener",
+ 0, GenericUtils.size(l.getFailedChannels()));
}
}
- private static <L extends ChannelListener & NamedResource> void addChannelListener(Map<String, L> listeners, ChannelListenerManager manager, L listener) {
+ private static <L extends ChannelListener & NamedResource> void addChannelListener(
+ Map<String, L> listeners, ChannelListenerManager manager, L listener) {
String name = listener.getName();
assertNull("Duplicate listener named " + name, listeners.put(name, listener));
manager.addChannelListener(listener);
@@ -378,7 +387,9 @@ public class ClientTest extends BaseTestSupport {
}
private ClientSession createTestClientSession(String host) throws IOException {
- ClientSession session = client.connect(getCurrentTestName(), host, port).verify(7L, TimeUnit.SECONDS).getSession();
+ ClientSession session = client.connect(getCurrentTestName(), host, port)
+ .verify(7L, TimeUnit.SECONDS)
+ .getSession();
try {
assertNotNull("Client session creation not signalled", clientSessionHolder.get());
session.addPasswordIdentity(getCurrentTestName());
[2/5] mina-sshd git commit: Fixed behavior of
AbstractSftpClient#handleUnexpectedPacket to match its signature
Posted by lg...@apache.org.
Fixed behavior of AbstractSftpClient#handleUnexpectedPacket to match its signature
Project: http://git-wip-us.apache.org/repos/asf/mina-sshd/repo
Commit: http://git-wip-us.apache.org/repos/asf/mina-sshd/commit/1a8924db
Tree: http://git-wip-us.apache.org/repos/asf/mina-sshd/tree/1a8924db
Diff: http://git-wip-us.apache.org/repos/asf/mina-sshd/diff/1a8924db
Branch: refs/heads/master
Commit: 1a8924db097928b4be51dd73a9a1311813d1b417
Parents: 775f349
Author: Lyor Goldstein <lg...@apache.org>
Authored: Sun Nov 18 09:27:51 2018 +0200
Committer: Lyor Goldstein <lg...@apache.org>
Committed: Sun Nov 18 11:32:53 2018 +0200
----------------------------------------------------------------------
.../subsystem/sftp/impl/AbstractSftpClient.java | 26 +++++++++++++++++---
.../subsystem/sftp/impl/DefaultSftpClient.java | 7 +++++-
2 files changed, 28 insertions(+), 5 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/1a8924db/sshd-sftp/src/main/java/org/apache/sshd/client/subsystem/sftp/impl/AbstractSftpClient.java
----------------------------------------------------------------------
diff --git a/sshd-sftp/src/main/java/org/apache/sshd/client/subsystem/sftp/impl/AbstractSftpClient.java b/sshd-sftp/src/main/java/org/apache/sshd/client/subsystem/sftp/impl/AbstractSftpClient.java
index d7b45a8..e10b466 100644
--- a/sshd-sftp/src/main/java/org/apache/sshd/client/subsystem/sftp/impl/AbstractSftpClient.java
+++ b/sshd-sftp/src/main/java/org/apache/sshd/client/subsystem/sftp/impl/AbstractSftpClient.java
@@ -182,8 +182,10 @@ public abstract class AbstractSftpClient extends AbstractSubsystemClient impleme
String lang = buffer.getString();
checkResponseStatus(cmd, id, substatus, msg, lang);
} else {
- //noinspection ThrowableResultOfMethodCallIgnored
- handleUnexpectedPacket(cmd, SftpConstants.SSH_FXP_STATUS, id, type, length, buffer);
+ IOException err = handleUnexpectedPacket(cmd, SftpConstants.SSH_FXP_STATUS, id, type, length, buffer);
+ if (err != null) {
+ throw err;
+ }
}
}
@@ -253,7 +255,11 @@ public abstract class AbstractSftpClient extends AbstractSubsystemClient impleme
}
protected byte[] handleUnexpectedHandlePacket(int cmd, int id, int type, int length, Buffer buffer) throws IOException {
- handleUnexpectedPacket(cmd, SftpConstants.SSH_FXP_HANDLE, id, type, length, buffer);
+ IOException err = handleUnexpectedPacket(cmd, SftpConstants.SSH_FXP_HANDLE, id, type, length, buffer);
+ if (err != null) {
+ throw err;
+ }
+
throw new SshException("No handling for unexpected handle packet id=" + id
+ ", type=" + SftpConstants.getCommandMessageName(type) + ", length=" + length);
}
@@ -1044,10 +1050,22 @@ public abstract class AbstractSftpClient extends AbstractSubsystemClient impleme
return Collections.emptyList();
}
+ /**
+ * @param cmd The initial command sent
+ * @param expected The expected packet type
+ * @param id The reported identifier
+ * @param type The reported SFTP response type
+ * @param length The packet length
+ * @param buffer The {@link Buffer} after reading from it whatever data
+ * led to this call
+ * @return The exception to throw - if {@code null} then implementor assumed
+ * to handle the exception internal. Otherwise, the exception is re-thrown
+ * @throws IOException If failed to handle the exception internally
+ */
protected IOException handleUnexpectedPacket(
int cmd, int expected, int id, int type, int length, Buffer buffer)
throws IOException {
- throw new SshException("Unexpected SFTP packet received while awaiting " + SftpConstants.getCommandMessageName(expected)
+ return new SshException("Unexpected SFTP packet received while awaiting " + SftpConstants.getCommandMessageName(expected)
+ " response to " + SftpConstants.getCommandMessageName(cmd)
+ ": type=" + SftpConstants.getCommandMessageName(type) + ", id=" + id + ", length=" + length);
}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/1a8924db/sshd-sftp/src/main/java/org/apache/sshd/client/subsystem/sftp/impl/DefaultSftpClient.java
----------------------------------------------------------------------
diff --git a/sshd-sftp/src/main/java/org/apache/sshd/client/subsystem/sftp/impl/DefaultSftpClient.java b/sshd-sftp/src/main/java/org/apache/sshd/client/subsystem/sftp/impl/DefaultSftpClient.java
index 197809e..b1d095d 100644
--- a/sshd-sftp/src/main/java/org/apache/sshd/client/subsystem/sftp/impl/DefaultSftpClient.java
+++ b/sshd-sftp/src/main/java/org/apache/sshd/client/subsystem/sftp/impl/DefaultSftpClient.java
@@ -413,7 +413,12 @@ public class DefaultSftpClient extends AbstractSftpClient {
throwStatusException(SftpConstants.SSH_FXP_INIT, id, substatus, msg, lang);
} else {
- handleUnexpectedPacket(SftpConstants.SSH_FXP_INIT, SftpConstants.SSH_FXP_VERSION, id, type, length, buffer);
+ IOException err = handleUnexpectedPacket(
+ SftpConstants.SSH_FXP_INIT, SftpConstants.SSH_FXP_VERSION, id, type, length, buffer);
+ if (err != null) {
+ throw err;
+ }
+
}
}
[3/5] mina-sshd git commit: [SSHD-868] Added validations for length
and/or count data extracted for packet buffers
Posted by lg...@apache.org.
[SSHD-868] Added validations for length and/or count data extracted for packet buffers
Project: http://git-wip-us.apache.org/repos/asf/mina-sshd/repo
Commit: http://git-wip-us.apache.org/repos/asf/mina-sshd/commit/db6e5b53
Tree: http://git-wip-us.apache.org/repos/asf/mina-sshd/tree/db6e5b53
Diff: http://git-wip-us.apache.org/repos/asf/mina-sshd/diff/db6e5b53
Branch: refs/heads/master
Commit: db6e5b5344dcf687ad01a6d7bc94cfa531809d37
Parents: a6dffd5
Author: Lyor Goldstein <lg...@apache.org>
Authored: Sun Nov 18 08:41:05 2018 +0200
Committer: Lyor Goldstein <lg...@apache.org>
Committed: Sun Nov 18 11:32:53 2018 +0200
----------------------------------------------------------------------
.../org/apache/sshd/common/SshConstants.java | 9 +++
.../apache/sshd/common/util/buffer/Buffer.java | 31 +++++++--
.../common/util/buffer/ByteArrayBuffer.java | 15 ++--
.../sshd/agent/common/AbstractAgentClient.java | 7 +-
.../sshd/agent/common/AbstractAgentProxy.java | 6 +-
.../org/apache/sshd/agent/unix/AgentClient.java | 7 +-
.../keyboard/UserAuthKeyboardInteractive.java | 20 ++++--
.../common/session/helpers/AbstractSession.java | 8 ++-
.../sshd/server/auth/gss/UserAuthGSS.java | 72 +++++++++++---------
.../auth/hostbased/UserAuthHostBased.java | 12 +++-
.../keyboard/UserAuthKeyboardInteractive.java | 16 +++--
.../server/auth/pubkey/UserAuthPublicKey.java | 12 +++-
.../org/apache/sshd/server/kex/DHGEXServer.java | 4 +-
.../deprecated/UserAuthKeyboardInteractive.java | 19 ++++--
.../helpers/AbstractSftpClientExtension.java | 14 ++--
.../subsystem/sftp/impl/AbstractSftpClient.java | 37 +++++++---
.../subsystem/sftp/impl/DefaultSftpClient.java | 4 ++
.../sshd/common/subsystem/sftp/SftpHelper.java | 51 +++++++++-----
.../sftp/extensions/AclSupportedParser.java | 5 ++
.../server/subsystem/sftp/SftpSubsystem.java | 3 +-
20 files changed, 240 insertions(+), 112 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/db6e5b53/sshd-common/src/main/java/org/apache/sshd/common/SshConstants.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/SshConstants.java b/sshd-common/src/main/java/org/apache/sshd/common/SshConstants.java
index ee8ae63..57f3c17 100644
--- a/sshd-common/src/main/java/org/apache/sshd/common/SshConstants.java
+++ b/sshd-common/src/main/java/org/apache/sshd/common/SshConstants.java
@@ -122,6 +122,15 @@ public final class SshConstants {
// Some more constants
public static final int SSH_EXTENDED_DATA_STDERR = 1; // see RFC4254 section 5.2
public static final int SSH_PACKET_HEADER_LEN = 5; // 32-bit length + 8-bit pad length
+ /*
+ * See https://tools.ietf.org/html/rfc4253#section-6.1:
+ *
+ * All implementations MUST be able to process packets with an
+ * uncompressed payload length of 32768 bytes or less and a total packet
+ * size of 35000 bytes or less
+ */
+ public static final int SSH_REQUIRED_PAYLOAD_PACKET_LENGTH_SUPPORT = 32768;
+ public static final int SSH_REQUIRED_TOTAL_PACKET_LENGTH_SUPPORT = 35000;
private SshConstants() {
throw new UnsupportedOperationException("No instance allowed");
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/db6e5b53/sshd-common/src/main/java/org/apache/sshd/common/util/buffer/Buffer.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/buffer/Buffer.java b/sshd-common/src/main/java/org/apache/sshd/common/util/buffer/Buffer.java
index dd61473..8b950d5 100644
--- a/sshd-common/src/main/java/org/apache/sshd/common/util/buffer/Buffer.java
+++ b/sshd-common/src/main/java/org/apache/sshd/common/util/buffer/Buffer.java
@@ -56,6 +56,7 @@ import java.util.function.IntUnaryOperator;
import java.util.logging.Level;
import org.apache.sshd.common.PropertyResolver;
+import org.apache.sshd.common.SshConstants;
import org.apache.sshd.common.SshException;
import org.apache.sshd.common.cipher.ECCurves;
import org.apache.sshd.common.config.keys.KeyUtils;
@@ -313,12 +314,15 @@ public abstract class Buffer implements Readable {
* @see #getString(Charset)
*/
public List<String> getStringList(int count, Charset charset) {
+ if ((count < 0) || (count > SshConstants.SSH_REQUIRED_PAYLOAD_PACKET_LENGTH_SUPPORT)) {
+ throw new IndexOutOfBoundsException("Illogical string list length: " + count);
+ }
if (count == 0) {
return Collections.emptyList();
}
List<String> list = new ArrayList<>(count);
- for (int index = 0; index < count; index++) {
+ for (int index = 1; index <= count; index++) {
String s = getString(charset);
list.add(s);
}
@@ -337,11 +341,7 @@ public abstract class Buffer implements Readable {
}
public byte[] getBytes() {
- int len = getInt();
- if (len < 0) {
- throw new BufferException("Bad item length: " + len);
- }
- ensureAvailable(len);
+ int len = ensureAvailable(getInt());
byte[] b = new byte[len];
getRawBytes(b);
return b;
@@ -365,6 +365,10 @@ public abstract class Buffer implements Readable {
public PublicKey getPublicKey(BufferPublicKeyParser<? extends PublicKey> parser) throws SshException {
int ow = wpos();
int len = getInt();
+ if (len < 0) {
+ throw new SshException("Illogical public key length: " + len);
+ }
+
wpos(rpos() + len);
try {
return getRawPublicKey(parser);
@@ -470,11 +474,24 @@ public abstract class Buffer implements Readable {
return new KeyPair(pubKey, privKey);
}
- public void ensureAvailable(int reqLen) throws BufferException {
+ /**
+ * Makes sure the buffer contains enough data to accommodate the requested length
+ *
+ * @param reqLen Requested data in bytes
+ * @return Same as input if validation successful
+ * @throws BufferException If negative length or beyond available requested
+ */
+ public int ensureAvailable(int reqLen) throws BufferException {
+ if (reqLen < 0) {
+ throw new BufferException("Bad item length: " + reqLen);
+ }
+
int availLen = available();
if (availLen < reqLen) {
throw new BufferException("Underflow: requested=" + reqLen + ", available=" + availLen);
}
+
+ return reqLen;
}
/*======================
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/db6e5b53/sshd-common/src/main/java/org/apache/sshd/common/util/buffer/ByteArrayBuffer.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/buffer/ByteArrayBuffer.java b/sshd-common/src/main/java/org/apache/sshd/common/util/buffer/ByteArrayBuffer.java
index 6655ec1..49fef32 100644
--- a/sshd-common/src/main/java/org/apache/sshd/common/util/buffer/ByteArrayBuffer.java
+++ b/sshd-common/src/main/java/org/apache/sshd/common/util/buffer/ByteArrayBuffer.java
@@ -68,6 +68,9 @@ public class ByteArrayBuffer extends Buffer {
}
public ByteArrayBuffer(byte[] data, int off, int len, boolean read) {
+ if ((off < 0) || (len < 0)) {
+ throw new IndexOutOfBoundsException("Invalid offset(" + off + ")/length(" + len + ")");
+ }
this.data = data;
this.rpos = off;
this.wpos = (read ? len : 0) + off;
@@ -171,13 +174,9 @@ public class ByteArrayBuffer extends Buffer {
@Override
public String getString(Charset charset) {
- int len = getInt();
- if (len < 0) {
- throw new BufferException("Bad item length: " + len);
- }
- ensureAvailable(len);
-
Objects.requireNonNull(charset, "No charset specified");
+
+ int len = ensureAvailable(getInt());
String s = new String(data, rpos, len, charset);
rpos += len;
return s;
@@ -192,6 +191,10 @@ public class ByteArrayBuffer extends Buffer {
@Override
protected void copyRawBytes(int offset, byte[] buf, int pos, int len) {
+ if ((offset < 0) || (pos < 0) || (len < 0)) {
+ throw new IndexOutOfBoundsException(
+ "Invalid offset(" + offset + ")/position(" + pos + ")/length(" + len + ") required");
+ }
System.arraycopy(data, rpos + offset, buf, pos, len);
}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/db6e5b53/sshd-core/src/main/java/org/apache/sshd/agent/common/AbstractAgentClient.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/agent/common/AbstractAgentClient.java b/sshd-core/src/main/java/org/apache/sshd/agent/common/AbstractAgentClient.java
index 07d04ce..c614494 100644
--- a/sshd-core/src/main/java/org/apache/sshd/agent/common/AbstractAgentClient.java
+++ b/sshd-core/src/main/java/org/apache/sshd/agent/common/AbstractAgentClient.java
@@ -19,6 +19,7 @@
package org.apache.sshd.agent.common;
import java.io.IOException;
+import java.io.StreamCorruptedException;
import java.security.KeyPair;
import java.security.PublicKey;
import java.util.Collection;
@@ -55,6 +56,10 @@ public abstract class AbstractAgentClient extends AbstractLoggingBean {
int rpos = buffer.rpos();
int len = buffer.getInt();
+ // Protect against malicious or corrupted packets
+ if (len < 0) {
+ throw new StreamCorruptedException("Illogical message length: " + len);
+ }
buffer.rpos(rpos);
avail = buffer.available();
@@ -77,7 +82,7 @@ public abstract class AbstractAgentClient extends AbstractLoggingBean {
} catch (Exception e) {
if (log.isDebugEnabled()) {
log.debug("Failed ({}) to handle command={}: {}",
- e.getClass().getSimpleName(), cmd, e.getMessage());
+ e.getClass().getSimpleName(), cmd, e.getMessage());
}
if (log.isTraceEnabled()) {
log.trace("Received command=" + cmd + " handling failure details", e);
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/db6e5b53/sshd-core/src/main/java/org/apache/sshd/agent/common/AbstractAgentProxy.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/agent/common/AbstractAgentProxy.java b/sshd-core/src/main/java/org/apache/sshd/agent/common/AbstractAgentProxy.java
index 717569d..27c2112 100644
--- a/sshd-core/src/main/java/org/apache/sshd/agent/common/AbstractAgentProxy.java
+++ b/sshd-core/src/main/java/org/apache/sshd/agent/common/AbstractAgentProxy.java
@@ -83,8 +83,8 @@ public abstract class AbstractAgentProxy extends AbstractLoggingBean implements
}
int nbIdentities = buffer.getInt();
- if (nbIdentities > 1024) {
- throw new SshException("Bad identities count: " + nbIdentities);
+ if ((nbIdentities < 0) || (nbIdentities > 1024)) {
+ throw new SshException("Illogical identities count: " + nbIdentities);
}
List<SimpleImmutableEntry<PublicKey, String>> keys = new ArrayList<>(nbIdentities);
@@ -94,7 +94,7 @@ public abstract class AbstractAgentProxy extends AbstractLoggingBean implements
String comment = buffer.getString();
if (debugEnabled) {
log.debug("getIdentities() key type={}, comment={}, fingerprint={}",
- KeyUtils.getKeyType(key), comment, KeyUtils.getFingerPrint(key));
+ KeyUtils.getKeyType(key), comment, KeyUtils.getFingerPrint(key));
}
keys.add(new SimpleImmutableEntry<>(key, comment));
}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/db6e5b53/sshd-core/src/main/java/org/apache/sshd/agent/unix/AgentClient.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/agent/unix/AgentClient.java b/sshd-core/src/main/java/org/apache/sshd/agent/unix/AgentClient.java
index 3291092..c6d3585 100644
--- a/sshd-core/src/main/java/org/apache/sshd/agent/unix/AgentClient.java
+++ b/sshd-core/src/main/java/org/apache/sshd/agent/unix/AgentClient.java
@@ -20,6 +20,7 @@ package org.apache.sshd.agent.unix;
import java.io.IOException;
import java.io.InterruptedIOException;
+import java.io.StreamCorruptedException;
import java.util.Queue;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.Future;
@@ -118,8 +119,12 @@ public class AgentClient extends AbstractAgentProxy implements Runnable {
if (receiveBuffer.available() >= 4) {
int rpos = receiveBuffer.rpos();
int len = receiveBuffer.getInt();
+ // Protect against malicious or corrupted packets
+ if (len < 0) {
+ throw new StreamCorruptedException("Illogical message length: " + len);
+ }
receiveBuffer.rpos(rpos);
- if (receiveBuffer.available() >= 4 + len) {
+ if (receiveBuffer.available() >= (4 + len)) {
message = new ByteArrayBuffer(receiveBuffer.getBytes());
receiveBuffer.compact();
}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/db6e5b53/sshd-core/src/main/java/org/apache/sshd/client/auth/keyboard/UserAuthKeyboardInteractive.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/auth/keyboard/UserAuthKeyboardInteractive.java b/sshd-core/src/main/java/org/apache/sshd/client/auth/keyboard/UserAuthKeyboardInteractive.java
index c27a4c4..96a204f 100644
--- a/sshd-core/src/main/java/org/apache/sshd/client/auth/keyboard/UserAuthKeyboardInteractive.java
+++ b/sshd-core/src/main/java/org/apache/sshd/client/auth/keyboard/UserAuthKeyboardInteractive.java
@@ -18,7 +18,6 @@
*/
package org.apache.sshd.client.auth.keyboard;
-import java.util.Arrays;
import java.util.Iterator;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
@@ -145,6 +144,13 @@ public class UserAuthKeyboardInteractive extends AbstractUserAuth {
String instruction = buffer.getString();
String lang = buffer.getString();
int num = buffer.getInt();
+ // Protect against malicious or corrupted packets
+ if ((num < 0) || (num > SshConstants.SSH_REQUIRED_PAYLOAD_PACKET_LENGTH_SUPPORT)) {
+ log.error("processAuthDataRequest({})[{}] illogical challenges count ({}) for name={}, instruction={}",
+ session, service, num, name, instruction);
+ throw new IndexOutOfBoundsException("Illogical challenges count: " + num);
+ }
+
boolean debugEnabled = log.isDebugEnabled();
if (debugEnabled) {
log.debug("processAuthDataRequest({})[{}] SSH_MSG_USERAUTH_INFO_REQUEST name={}, instruction={}, language={}, num-prompts={}",
@@ -159,16 +165,16 @@ public class UserAuthKeyboardInteractive extends AbstractUserAuth {
String[] prompt = (num > 0) ? new String[num] : GenericUtils.EMPTY_STRING_ARRAY;
boolean[] echo = (num > 0) ? new boolean[num] : GenericUtils.EMPTY_BOOLEAN_ARRAY;
+ boolean traceEnabled = log.isTraceEnabled();
for (int i = 0; i < num; i++) {
- // according to RFC4256: "The prompt field(s) MUST NOT be empty strings."
+ // TODO according to RFC4256: "The prompt field(s) MUST NOT be empty strings."
prompt[i] = buffer.getString();
echo[i] = buffer.getBoolean();
- }
- boolean traceEnabled = log.isTraceEnabled();
- if (traceEnabled) {
- log.trace("processAuthDataRequest({})[{}] Prompt: {}", session, service, Arrays.toString(prompt));
- log.trace("processAuthDataRequest({})[{}] Echo: {}", session, service, Arrays.toString(echo));
+ if (traceEnabled) {
+ log.trace("processAuthDataRequest({})[{}]({}) {}/{}: echo={}, prompt={}",
+ session, service, name, i + 1, num, echo[i], prompt[i]);
+ }
}
String[] rep = getUserResponses(name, instruction, lang, prompt, echo);
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/db6e5b53/sshd-core/src/main/java/org/apache/sshd/common/session/helpers/AbstractSession.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/session/helpers/AbstractSession.java b/sshd-core/src/main/java/org/apache/sshd/common/session/helpers/AbstractSession.java
index fc9958f..738a1a4 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/session/helpers/AbstractSession.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/session/helpers/AbstractSession.java
@@ -1014,8 +1014,12 @@ public abstract class AbstractSession extends SessionHelper {
}
// Read packet length
decoderLength = decoderBuffer.getInt();
- // Check packet length validity
- if ((decoderLength < SshConstants.SSH_PACKET_HEADER_LEN) || (decoderLength > (256 * 1024))) {
+ /*
+ * Check packet length validity - we allow 8 times the minimum required packet length support
+ * in order to be aligned with some OpenSSH versions that allow up to 256k
+ */
+ if ((decoderLength < SshConstants.SSH_PACKET_HEADER_LEN)
+ || (decoderLength > (8 * SshConstants.SSH_REQUIRED_PAYLOAD_PACKET_LENGTH_SUPPORT))) {
log.warn("decode({}) Error decoding packet(invalid length): {}", this, decoderLength);
decoderBuffer.dumpHex(getSimplifiedLogger(), "decode(" + this + ") invalid length packet", this);
throw new SshException(SshConstants.SSH2_DISCONNECT_PROTOCOL_ERROR,
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/db6e5b53/sshd-core/src/main/java/org/apache/sshd/server/auth/gss/UserAuthGSS.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/server/auth/gss/UserAuthGSS.java b/sshd-core/src/main/java/org/apache/sshd/server/auth/gss/UserAuthGSS.java
index db1256f..38bfd4f 100644
--- a/sshd-core/src/main/java/org/apache/sshd/server/auth/gss/UserAuthGSS.java
+++ b/sshd-core/src/main/java/org/apache/sshd/server/auth/gss/UserAuthGSS.java
@@ -32,6 +32,7 @@ import org.ietf.jgss.GSSContext;
import org.ietf.jgss.GSSCredential;
import org.ietf.jgss.GSSException;
import org.ietf.jgss.GSSManager;
+import org.ietf.jgss.GSSName;
import org.ietf.jgss.MessageProp;
import org.ietf.jgss.Oid;
@@ -62,60 +63,66 @@ public class UserAuthGSS extends AbstractUserAuth {
protected Boolean doAuth(Buffer buffer, boolean initial) throws Exception {
ServerSession session = getServerSession();
GSSAuthenticator auth = Objects.requireNonNull(session.getGSSAuthenticator(), "No GSSAuthenticator configured");
-
+ String user = getUsername();
boolean debugEnabled = log.isDebugEnabled();
+
if (initial) {
// Get mechanism count from buffer and look for Kerberos 5.
-
int num = buffer.getInt();
+ // Protect against malicious or corrupted packets
+ if ((num < 0) || (num > SshConstants.SSH_REQUIRED_PAYLOAD_PACKET_LENGTH_SUPPORT)) {
+ log.error("doAuth({}@{}) Illogical OID entries count: {}", user, session, num);
+ throw new IndexOutOfBoundsException("Illogical OID entries count: " + num);
+ }
- for (int i = 0; i < num; i++) {
+ boolean traceEnabled = log.isTraceEnabled();
+ for (int i = 1; i <= num; i++) {
Oid oid = new Oid(buffer.getBytes());
-
- if (oid.equals(KRB5_MECH)) {
- if (debugEnabled) {
- log.debug("doAuth({}@{}) found Kerberos 5", getUsername(), session);
- }
-
- // Validate initial user before proceeding
-
- if (!auth.validateInitialUser(session, getUsername())) {
- return Boolean.FALSE;
+ if (!oid.equals(KRB5_MECH)) {
+ if (traceEnabled) {
+ log.trace("doAuth({}@{}) skip OID {}/{}: {}", user, session, i, num, oid);
}
+ continue;
+ }
+ if (debugEnabled) {
+ log.debug("doAuth({}@{}) found Kerberos 5 after {}/{} OID(s)", user, session, i, num);
+ }
- GSSManager mgr = auth.getGSSManager();
- GSSCredential creds = auth.getGSSCredential(mgr);
+ // Validate initial user before proceeding
+ if (!auth.validateInitialUser(session, user)) {
+ return Boolean.FALSE;
+ }
- if (creds == null) {
- return Boolean.FALSE;
- }
+ GSSManager mgr = auth.getGSSManager();
+ GSSCredential creds = auth.getGSSCredential(mgr);
+ if (creds == null) {
+ return Boolean.FALSE;
+ }
- context = mgr.createContext(creds);
+ context = mgr.createContext(creds);
- // Send the matching mechanism back to the client
+ // Send the matching mechanism back to the client
- byte[] out = oid.getDER();
- Buffer b = session.createBuffer(SshConstants.SSH_MSG_USERAUTH_INFO_REQUEST, out.length + Integer.SIZE);
- b.putBytes(out);
- session.writePacket(b);
+ byte[] out = oid.getDER();
+ Buffer b = session.createBuffer(SshConstants.SSH_MSG_USERAUTH_INFO_REQUEST, out.length + Integer.SIZE);
+ b.putBytes(out);
+ session.writePacket(b);
- return null;
- }
+ return null;
}
// No matching mechanism found
-
return Boolean.FALSE;
} else {
int msg = buffer.getUByte();
if (!((msg == SshConstants.SSH_MSG_USERAUTH_INFO_RESPONSE)
|| ((msg == SshConstants.SSH_MSG_USERAUTH_GSSAPI_MIC)) && context.isEstablished())) {
throw new SshException(SshConstants.SSH2_DISCONNECT_PROTOCOL_ERROR,
- "Packet not supported by user authentication method: " + SshConstants.getCommandMessageName(msg));
+ "Packet not supported by user authentication method: " + SshConstants.getCommandMessageName(msg));
}
if (debugEnabled) {
- log.debug("doAuth({}@{}) In krb5.next: msg = {}", getUsername(), session, SshConstants.getCommandMessageName(msg));
+ log.debug("doAuth({}@{}) In krb5.next: msg = {}", user, session, SshConstants.getCommandMessageName(msg));
}
// If the context is established, this must be a MIC message
@@ -145,7 +152,7 @@ public class UserAuthGSS extends AbstractUserAuth {
} catch (GSSException e) {
if (debugEnabled) {
log.debug("doAuth({}@{}) GSS verification {} error: {}",
- getUsername(), session, e.getClass().getSimpleName(), e.getMessage());
+ user, session, e.getClass().getSimpleName(), e.getMessage());
}
return Boolean.FALSE;
}
@@ -157,9 +164,10 @@ public class UserAuthGSS extends AbstractUserAuth {
// Validate identity if context is now established
if (established && (identity == null)) {
- identity = context.getSrcName().toString();
+ GSSName srcName = context.getSrcName();
+ identity = srcName.toString();
if (debugEnabled) {
- log.debug("doAuth({}@{}) GSS identity is {}", getUsername(), session, identity);
+ log.debug("doAuth({}@{}) GSS identity is {}", user, session, identity);
}
if (!auth.validateIdentity(session, identity)) {
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/db6e5b53/sshd-core/src/main/java/org/apache/sshd/server/auth/hostbased/UserAuthHostBased.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/server/auth/hostbased/UserAuthHostBased.java b/sshd-core/src/main/java/org/apache/sshd/server/auth/hostbased/UserAuthHostBased.java
index c191876..d724af4 100644
--- a/sshd-core/src/main/java/org/apache/sshd/server/auth/hostbased/UserAuthHostBased.java
+++ b/sshd-core/src/main/java/org/apache/sshd/server/auth/hostbased/UserAuthHostBased.java
@@ -81,14 +81,22 @@ public class UserAuthHostBased extends AbstractUserAuth implements SignatureFact
String keyType = buffer.getString();
int keyLen = buffer.getInt();
int keyOffset = buffer.rpos();
+ int remaining = buffer.available();
+ // Protect against malicious or corrupted packets
+ if ((keyLen < 0) || (keyLen > remaining)) {
+ log.error("doAuth({}@{}) Illogical {} key length={} (max. available={})",
+ username, session, keyType, keyLen, remaining);
+ throw new IndexOutOfBoundsException("Illogical " + keyType + " key length: " + keyLen);
+ }
Buffer buf = new ByteArrayBuffer(buffer.array(), keyOffset, keyLen, true);
PublicKey clientKey = buf.getRawPublicKey();
List<X509Certificate> certs = Collections.emptyList();
- if (buf.available() > 0) {
+ remaining = buf.available();
+ if (remaining > 0) {
CertificateFactory cf = SecurityUtils.getCertificateFactory("X.509");
certs = new ArrayList<>();
- try (ByteArrayInputStream bais = new ByteArrayInputStream(buf.array(), buf.rpos(), buf.available())) {
+ try (ByteArrayInputStream bais = new ByteArrayInputStream(buf.array(), buf.rpos(), remaining)) {
X509Certificate c = (X509Certificate) cf.generateCertificate(bais);
certs.add(c);
}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/db6e5b53/sshd-core/src/main/java/org/apache/sshd/server/auth/keyboard/UserAuthKeyboardInteractive.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/server/auth/keyboard/UserAuthKeyboardInteractive.java b/sshd-core/src/main/java/org/apache/sshd/server/auth/keyboard/UserAuthKeyboardInteractive.java
index f73cd5d..e7b9b63 100644
--- a/sshd-core/src/main/java/org/apache/sshd/server/auth/keyboard/UserAuthKeyboardInteractive.java
+++ b/sshd-core/src/main/java/org/apache/sshd/server/auth/keyboard/UserAuthKeyboardInteractive.java
@@ -111,12 +111,18 @@ public class UserAuthKeyboardInteractive extends AbstractUserAuth {
}
int num = buffer.getInt();
+ // Protect against malicious or corrupted packets
+ if ((num < 0) || (num > SshConstants.SSH_REQUIRED_PAYLOAD_PACKET_LENGTH_SUPPORT)) {
+ log.error("doValidateAuthResponse({}@{}) illogical response count: {}", username, session, num);
+ throw new IndexOutOfBoundsException("Illogical response count: " + num);
+ }
+
List<String> responses = (num <= 0) ? Collections.emptyList() : new ArrayList<>(num);
boolean traceEnabled = log.isTraceEnabled();
- for (int index = 0; index < num; index++) {
+ for (int index = 1; index <= num; index++) {
String value = buffer.getString();
if (traceEnabled) {
- log.trace("doAuth({}@{}) response #{}: {}", username, session, index + 1, value);
+ log.trace("doAuth({}@{}) response {}/{}: {}", username, session, index, num, value);
}
responses.add(value);
}
@@ -125,7 +131,7 @@ public class UserAuthKeyboardInteractive extends AbstractUserAuth {
if (auth == null) {
if (debugEnabled) {
log.debug("doAuth({}@{}) no interactive authenticator to validate {} responses",
- username, session, num);
+ username, session, num);
}
return false;
}
@@ -135,7 +141,7 @@ public class UserAuthKeyboardInteractive extends AbstractUserAuth {
authed = auth.authenticate(session, username, responses);
} catch (Error e) {
log.warn("doAuth({}@{}) failed ({}) to consult authenticator: {}",
- username, session, e.getClass().getSimpleName(), e.getMessage());
+ username, session, e.getClass().getSimpleName(), e.getMessage());
if (debugEnabled) {
log.debug("doAuth(" + username + "@" + session + ") authenticator consultation failure details", e);
}
@@ -144,7 +150,7 @@ public class UserAuthKeyboardInteractive extends AbstractUserAuth {
if (debugEnabled) {
log.debug("doAuth({}@{}) authenticate {} responses result: {}",
- username, session, num, authed);
+ username, session, num, authed);
}
return authed;
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/db6e5b53/sshd-core/src/main/java/org/apache/sshd/server/auth/pubkey/UserAuthPublicKey.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/server/auth/pubkey/UserAuthPublicKey.java b/sshd-core/src/main/java/org/apache/sshd/server/auth/pubkey/UserAuthPublicKey.java
index 0ea0dfa..29c3f5f 100644
--- a/sshd-core/src/main/java/org/apache/sshd/server/auth/pubkey/UserAuthPublicKey.java
+++ b/sshd-core/src/main/java/org/apache/sshd/server/auth/pubkey/UserAuthPublicKey.java
@@ -70,16 +70,24 @@ public class UserAuthPublicKey extends AbstractUserAuth implements SignatureFact
@Override
public Boolean doAuth(Buffer buffer, boolean init) throws Exception {
ValidateUtils.checkTrue(init, "Instance not initialized");
+ ServerSession session = getServerSession();
+ String username = getUsername();
boolean hasSig = buffer.getBoolean();
String alg = buffer.getString();
int oldLim = buffer.wpos();
int oldPos = buffer.rpos();
int len = buffer.getInt();
+ int remaining = buffer.available();
+ // Protect against malicious or corrupted packets
+ if ((len < 0) || (len > remaining)) {
+ log.error("doAuth({}@{}) illogical algorithm={} signature length ({}) when remaining={}",
+ username, session, alg, len, remaining);
+ throw new IndexOutOfBoundsException("Illogical signature length (" + len + ") for algorithm=" + alg);
+ }
+
buffer.wpos(buffer.rpos() + len);
- ServerSession session = getServerSession();
- String username = getUsername();
PublicKey key = buffer.getRawPublicKey();
Collection<NamedFactory<Signature>> factories =
ValidateUtils.checkNotNullAndNotEmpty(
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/db6e5b53/sshd-core/src/main/java/org/apache/sshd/server/kex/DHGEXServer.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/server/kex/DHGEXServer.java b/sshd-core/src/main/java/org/apache/sshd/server/kex/DHGEXServer.java
index 404059f..914c5b0 100644
--- a/sshd-core/src/main/java/org/apache/sshd/server/kex/DHGEXServer.java
+++ b/sshd-core/src/main/java/org/apache/sshd/server/kex/DHGEXServer.java
@@ -114,7 +114,7 @@ public class DHGEXServer extends AbstractDHServerKeyExchange {
prf = buffer.getInt();
max = SecurityUtils.getMaxDHGroupExchangeKeySize();
- if (max < min || prf < min || max < prf) {
+ if ((max < min) || (prf < min) || (max < prf)) {
throw new SshException(SshConstants.SSH2_DISCONNECT_KEY_EXCHANGE_FAILED,
"Protocol error: bad parameters " + min + " !< " + prf + " !< " + max);
}
@@ -140,7 +140,7 @@ public class DHGEXServer extends AbstractDHServerKeyExchange {
min = buffer.getInt();
prf = buffer.getInt();
max = buffer.getInt();
- if (prf < min || max < prf) {
+ if ((prf < min) || (max < prf)) {
throw new SshException(SshConstants.SSH2_DISCONNECT_KEY_EXCHANGE_FAILED,
"Protocol error: bad parameters " + min + " !< " + prf + " !< " + max);
}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/db6e5b53/sshd-core/src/test/java/org/apache/sshd/deprecated/UserAuthKeyboardInteractive.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/deprecated/UserAuthKeyboardInteractive.java b/sshd-core/src/test/java/org/apache/sshd/deprecated/UserAuthKeyboardInteractive.java
index f02f619..6236e05 100644
--- a/sshd-core/src/test/java/org/apache/sshd/deprecated/UserAuthKeyboardInteractive.java
+++ b/sshd-core/src/test/java/org/apache/sshd/deprecated/UserAuthKeyboardInteractive.java
@@ -24,6 +24,7 @@ import java.util.Arrays;
import org.apache.sshd.client.auth.keyboard.UserInteraction;
import org.apache.sshd.client.session.ClientSession;
import org.apache.sshd.common.SshConstants;
+import org.apache.sshd.common.util.GenericUtils;
import org.apache.sshd.common.util.buffer.Buffer;
/**
@@ -69,11 +70,18 @@ public class UserAuthKeyboardInteractive extends AbstractUserAuth {
String language_tag = buffer.getString();
if (debugEnabled) {
log.debug("next({}) Received SSH_MSG_USERAUTH_INFO_REQUEST - name={}, instruction={}, lang={}",
- session, name, instruction, language_tag);
+ session, name, instruction, language_tag);
}
int num = buffer.getInt();
- String[] prompt = new String[num];
- boolean[] echo = new boolean[num];
+ // Protect against malicious or corrupted packets
+ if ((num < 0) || (num > SshConstants.SSH_REQUIRED_PAYLOAD_PACKET_LENGTH_SUPPORT)) {
+ log.error("next({}) illogical challenges count ({}) for name={}, instruction={}",
+ session, num, name, instruction);
+ throw new IndexOutOfBoundsException("Illogical challenges count: " + num);
+ }
+
+ String[] prompt = (num <= 0) ? GenericUtils.EMPTY_STRING_ARRAY : new String[num];
+ boolean[] echo = (num <= 0) ? GenericUtils.EMPTY_BOOLEAN_ARRAY : new boolean[num];
for (int i = 0; i < num; i++) {
prompt[i] = buffer.getString();
echo[i] = buffer.getBoolean();
@@ -85,8 +93,9 @@ public class UserAuthKeyboardInteractive extends AbstractUserAuth {
String[] rep = null;
if (num == 0) {
- rep = new String[0];
- } else if (num == 1 && password != null && !echo[0] && prompt[0].toLowerCase().startsWith("password:")) {
+ rep = GenericUtils.EMPTY_STRING_ARRAY;
+ } else if ((num == 1) && (password != null) && (!echo[0])
+ && prompt[0].toLowerCase().startsWith("password:")) {
rep = new String[]{password};
} else {
UserInteraction ui = session.getUserInteraction();
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/db6e5b53/sshd-sftp/src/main/java/org/apache/sshd/client/subsystem/sftp/extensions/helpers/AbstractSftpClientExtension.java
----------------------------------------------------------------------
diff --git a/sshd-sftp/src/main/java/org/apache/sshd/client/subsystem/sftp/extensions/helpers/AbstractSftpClientExtension.java b/sshd-sftp/src/main/java/org/apache/sshd/client/subsystem/sftp/extensions/helpers/AbstractSftpClientExtension.java
index 6b179c9..6e1f52b 100644
--- a/sshd-sftp/src/main/java/org/apache/sshd/client/subsystem/sftp/extensions/helpers/AbstractSftpClientExtension.java
+++ b/sshd-sftp/src/main/java/org/apache/sshd/client/subsystem/sftp/extensions/helpers/AbstractSftpClientExtension.java
@@ -107,11 +107,9 @@ public abstract class AbstractSftpClientExtension extends AbstractLoggingBean im
/**
* @param buffer The {@link Buffer}
- * @param target A target path {@link String} or {@link Handle} or {@code byte[]}
- * to be encoded in the buffer
+ * @param target A target path {@link String} or {@link Handle} or {@code byte[]} to be encoded in the buffer
* @return The updated buffer
- * @throws UnsupportedOperationException If target is not one of the above
- * supported types
+ * @throws UnsupportedOperationException If target is not one of the above supported types
*/
public Buffer putTarget(Buffer buffer, Object target) {
if (target instanceof CharSequence) {
@@ -128,8 +126,7 @@ public abstract class AbstractSftpClientExtension extends AbstractLoggingBean im
}
/**
- * @param target A target path {@link String} or {@link Handle} or {@code byte[]}
- * to be encoded in the buffer
+ * @param target A target path {@link String} or {@link Handle} or {@code byte[]} to be encoded in the buffer
* @return A {@link Buffer} with the extension name set
* @see #getCommandBuffer(Object, int)
*/
@@ -138,8 +135,7 @@ public abstract class AbstractSftpClientExtension extends AbstractLoggingBean im
}
/**
- * @param target A target path {@link String} or {@link Handle} or {@code byte[]}
- * to be encoded in the buffer
+ * @param target A target path {@link String} or {@link Handle} or {@code byte[]} to be encoded in the buffer
* @param extraSize Extra size - beyond the path/handle to be allocated
* @return A {@link Buffer} with the extension name set
* @see #getCommandBuffer(int)
@@ -173,7 +169,7 @@ public abstract class AbstractSftpClientExtension extends AbstractLoggingBean im
* or {@code null} if this is a {@link SftpConstants#SSH_FXP_STATUS} carrying
* an {@link SftpConstants#SSH_FX_OK} result
* @throws IOException If a non-{@link SftpConstants#SSH_FX_OK} result or
- * not a {@link SftpConstants#SSH_FXP_EXTENDED_REPLY} buffer
+ * not a {@link SftpConstants#SSH_FXP_EXTENDED_REPLY} buffer
*/
protected Buffer checkExtendedReplyBuffer(Buffer buffer) throws IOException {
int length = buffer.getInt();
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/db6e5b53/sshd-sftp/src/main/java/org/apache/sshd/client/subsystem/sftp/impl/AbstractSftpClient.java
----------------------------------------------------------------------
diff --git a/sshd-sftp/src/main/java/org/apache/sshd/client/subsystem/sftp/impl/AbstractSftpClient.java b/sshd-sftp/src/main/java/org/apache/sshd/client/subsystem/sftp/impl/AbstractSftpClient.java
index 2ef5039..3d607f3 100644
--- a/sshd-sftp/src/main/java/org/apache/sshd/client/subsystem/sftp/impl/AbstractSftpClient.java
+++ b/sshd-sftp/src/main/java/org/apache/sshd/client/subsystem/sftp/impl/AbstractSftpClient.java
@@ -38,6 +38,7 @@ import org.apache.sshd.client.subsystem.sftp.SftpClient;
import org.apache.sshd.client.subsystem.sftp.extensions.BuiltinSftpClientExtensions;
import org.apache.sshd.client.subsystem.sftp.extensions.SftpClientExtension;
import org.apache.sshd.client.subsystem.sftp.extensions.SftpClientExtensionFactory;
+import org.apache.sshd.common.SshConstants;
import org.apache.sshd.common.SshException;
import org.apache.sshd.common.channel.Channel;
import org.apache.sshd.common.subsystem.sftp.SftpConstants;
@@ -770,7 +771,9 @@ public abstract class AbstractSftpClient extends AbstractSubsystemClient impleme
return checkData(SftpConstants.SSH_FXP_READ, buffer, dstOffset, dst, eofSignalled);
}
- protected int checkData(int cmd, Buffer request, int dstOffset, byte[] dst, AtomicReference<Boolean> eofSignalled) throws IOException {
+ protected int checkData(
+ int cmd, Buffer request, int dstOffset, byte[] dst, AtomicReference<Boolean> eofSignalled)
+ throws IOException {
if (eofSignalled != null) {
eofSignalled.set(null);
}
@@ -779,7 +782,9 @@ public abstract class AbstractSftpClient extends AbstractSubsystemClient impleme
return checkDataResponse(cmd, response, dstOffset, dst, eofSignalled);
}
- protected int checkDataResponse(int cmd, Buffer buffer, int dstoff, byte[] dst, AtomicReference<Boolean> eofSignalled) throws IOException {
+ protected int checkDataResponse(
+ int cmd, Buffer buffer, int dstoff, byte[] dst, AtomicReference<Boolean> eofSignalled)
+ throws IOException {
if (eofSignalled != null) {
eofSignalled.set(null);
}
@@ -944,17 +949,23 @@ public abstract class AbstractSftpClient extends AbstractSubsystemClient impleme
int id = buffer.getInt();
boolean traceEnabled = log.isTraceEnabled();
if (type == SftpConstants.SSH_FXP_NAME) {
- int len = buffer.getInt();
- int version = getVersion();
ClientChannel channel = getClientChannel();
+ int count = buffer.getInt();
+ int version = getVersion();
+ // Protect against malicious or corrupted packets
+ if ((count < 0) || (count > SshConstants.SSH_REQUIRED_PAYLOAD_PACKET_LENGTH_SUPPORT)) {
+ log.error("checkDirResponse({})[id={}] illogical dir entries count: {}", channel, id, count);
+ throw new SshException("Illogical dir entries count: " + count);
+ }
+
boolean debugEnabled = log.isDebugEnabled();
if (debugEnabled) {
- log.debug("checkDirResponse({}}[id={}] reading {} entries", channel, id, len);
+ log.debug("checkDirResponse({}}[id={}] reading {} entries", channel, id, count);
}
- List<DirEntry> entries = new ArrayList<>(len);
+ List<DirEntry> entries = new ArrayList<>(count);
AtomicInteger nameIndex = new AtomicInteger(0);
- for (int i = 0; i < len; i++) {
+ for (int index = 1; index <= count; index++) {
String name = getReferencedName(cmd, buffer, nameIndex.getAndIncrement());
String longName = null;
if (version == SftpConstants.SFTP_V3) {
@@ -963,8 +974,8 @@ public abstract class AbstractSftpClient extends AbstractSubsystemClient impleme
Attributes attrs = readAttributes(cmd, buffer, nameIndex);
if (traceEnabled) {
- log.trace("checkDirResponse({})[id={}][{}] ({})[{}]: {}",
- channel, id, i, name, longName, attrs);
+ log.trace("checkDirResponse({})[id={}][{}/{}] ({})[{}]: {}",
+ channel, id, index, count, name, longName, attrs);
}
entries.add(new DirEntry(name, longName, attrs));
@@ -1000,7 +1011,9 @@ public abstract class AbstractSftpClient extends AbstractSubsystemClient impleme
return handleUnknownDirListingPacket(cmd, id, type, length, buffer);
}
- protected List<DirEntry> handleUnknownDirListingPacket(int cmd, int id, int type, int length, Buffer buffer) throws IOException {
+ protected List<DirEntry> handleUnknownDirListingPacket(
+ int cmd, int id, int type, int length, Buffer buffer)
+ throws IOException {
IOException err = handleUnexpectedPacket(cmd, SftpConstants.SSH_FXP_NAME, id, type, length, buffer);
if (err != null) {
throw err;
@@ -1008,7 +1021,9 @@ public abstract class AbstractSftpClient extends AbstractSubsystemClient impleme
return Collections.emptyList();
}
- protected IOException handleUnexpectedPacket(int cmd, int expected, int id, int type, int length, Buffer buffer) throws IOException {
+ protected IOException handleUnexpectedPacket(
+ int cmd, int expected, int id, int type, int length, Buffer buffer)
+ throws IOException {
throw new SshException("Unexpected SFTP packet received while awaiting " + SftpConstants.getCommandMessageName(expected)
+ " response to " + SftpConstants.getCommandMessageName(cmd)
+ ": type=" + SftpConstants.getCommandMessageName(type) + ", id=" + id + ", length=" + length);
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/db6e5b53/sshd-sftp/src/main/java/org/apache/sshd/client/subsystem/sftp/impl/DefaultSftpClient.java
----------------------------------------------------------------------
diff --git a/sshd-sftp/src/main/java/org/apache/sshd/client/subsystem/sftp/impl/DefaultSftpClient.java b/sshd-sftp/src/main/java/org/apache/sshd/client/subsystem/sftp/impl/DefaultSftpClient.java
index 2ac227c..197809e 100644
--- a/sshd-sftp/src/main/java/org/apache/sshd/client/subsystem/sftp/impl/DefaultSftpClient.java
+++ b/sshd-sftp/src/main/java/org/apache/sshd/client/subsystem/sftp/impl/DefaultSftpClient.java
@@ -46,6 +46,7 @@ import org.apache.sshd.client.channel.ClientChannel;
import org.apache.sshd.client.session.ClientSession;
import org.apache.sshd.client.subsystem.sftp.SftpVersionSelector;
import org.apache.sshd.common.PropertyResolverUtils;
+import org.apache.sshd.common.SshConstants;
import org.apache.sshd.common.SshException;
import org.apache.sshd.common.subsystem.sftp.SftpConstants;
import org.apache.sshd.common.subsystem.sftp.extensions.ParserUtils;
@@ -215,6 +216,9 @@ public class DefaultSftpClient extends AbstractSftpClient {
if (length < 5) {
throw new IOException("Illegal sftp packet length: " + length);
}
+ if (length > (8 * SshConstants.SSH_REQUIRED_PAYLOAD_PACKET_LENGTH_SUPPORT)) {
+ throw new StreamCorruptedException("Illogical sftp packet length: " + length);
+ }
if ((wpos - rpos) >= (length + 4)) {
incoming.rpos(rpos);
incoming.wpos(rpos + 4 + length);
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/db6e5b53/sshd-sftp/src/main/java/org/apache/sshd/common/subsystem/sftp/SftpHelper.java
----------------------------------------------------------------------
diff --git a/sshd-sftp/src/main/java/org/apache/sshd/common/subsystem/sftp/SftpHelper.java b/sshd-sftp/src/main/java/org/apache/sshd/common/subsystem/sftp/SftpHelper.java
index 8a6f9a3..ee43ab3 100644
--- a/sshd-sftp/src/main/java/org/apache/sshd/common/subsystem/sftp/SftpHelper.java
+++ b/sshd-sftp/src/main/java/org/apache/sshd/common/subsystem/sftp/SftpHelper.java
@@ -54,6 +54,7 @@ import java.util.TreeMap;
import java.util.concurrent.TimeUnit;
import org.apache.sshd.common.PropertyResolver;
+import org.apache.sshd.common.SshConstants;
import org.apache.sshd.common.util.GenericUtils;
import org.apache.sshd.common.util.OsUtils;
import org.apache.sshd.common.util.ValidateUtils;
@@ -640,9 +641,14 @@ public final class SftpHelper {
public static NavigableMap<String, byte[]> readExtensions(Buffer buffer) {
int count = buffer.getInt();
+ // Protect against malicious or malformed packets
+ if ((count < 0) || (count > SshConstants.SSH_REQUIRED_PAYLOAD_PACKET_LENGTH_SUPPORT)) {
+ throw new IndexOutOfBoundsException("Illogical extensions count: " + count);
+ }
+
// NOTE
NavigableMap<String, byte[]> extended = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
- for (int i = 0; i < count; i++) {
+ for (int i = 1; i <= count; i++) {
String key = buffer.getString();
byte[] val = buffer.getBytes();
byte[] prev = extended.put(key, val);
@@ -659,16 +665,16 @@ public final class SftpHelper {
return buffer;
}
- extensions.forEach((key, value) -> {
- Objects.requireNonNull(key, "No extension type");
- Objects.requireNonNull(value, "No extension value");
+ for (Map.Entry<?, ?> ee : extensions.entrySet()) {
+ Object key = Objects.requireNonNull(ee.getKey(), "No extension type");
+ Object value = Objects.requireNonNull(ee.getValue(), "No extension value");
buffer.putString(key.toString());
if (value instanceof byte[]) {
buffer.putBytes((byte[]) value);
} else {
buffer.putString(value.toString());
}
- });
+ }
return buffer;
}
@@ -680,11 +686,12 @@ public final class SftpHelper {
// NOTE: even though extensions are probably case sensitive we do not allow duplicate name that differs only in case
NavigableMap<String, String> map = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
- extensions.forEach((key, value) -> {
- ValidateUtils.checkNotNull(value, "No value for extension=%s", key);
- String prev = map.put(key, (value instanceof byte[]) ? new String((byte[]) value, StandardCharsets.UTF_8) : value.toString());
+ for (Map.Entry<?, ?> ee : extensions.entrySet()) {
+ Object key = Objects.requireNonNull(ee.getKey(), "No extension type");
+ Object value = ValidateUtils.checkNotNull(ee.getValue(), "No value for extension=%s", key);
+ String prev = map.put(key.toString(), (value instanceof byte[]) ? new String((byte[]) value, StandardCharsets.UTF_8) : value.toString());
ValidateUtils.checkTrue(prev == null, "Multiple values for extension=%s", key);
- });
+ }
return map;
}
@@ -709,6 +716,11 @@ public final class SftpHelper {
// for v6 see https://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#page-21
public static List<AclEntry> readACLs(Buffer buffer, int version) {
int aclSize = buffer.getInt();
+ // Protect against malicious or malformed packets
+ if ((aclSize < 0) || (aclSize > (2 * SshConstants.SSH_REQUIRED_PAYLOAD_PACKET_LENGTH_SUPPORT))) {
+ throw new IndexOutOfBoundsException("Illogical ACL entries size: " + aclSize);
+ }
+
int startPos = buffer.rpos();
Buffer aclBuffer = new ByteArrayBuffer(buffer.array(), startPos, aclSize, true);
List<AclEntry> acl = decodeACLs(aclBuffer, version);
@@ -724,14 +736,21 @@ public final class SftpHelper {
}
int count = buffer.getInt();
- // NOTE: although the value is defined as UINT32 we do not expected a count greater than Integer.MAX_VALUE
+ /*
+ * NOTE: although the value is defined as UINT32 we do not expected a count greater
+ * than several hundreds + protect against malicious or corrupted packets
+ */
+ if ((count < 0) || (count > SshConstants.SSH_REQUIRED_PAYLOAD_PACKET_LENGTH_SUPPORT)) {
+ throw new IndexOutOfBoundsException("Illogical ACL entries count: " + count);
+ }
+
ValidateUtils.checkTrue(count >= 0, "Invalid ACL entries count: %d", count);
if (count == 0) {
return Collections.emptyList();
}
List<AclEntry> acls = new ArrayList<>(count);
- for (int i = 0; i < count; i++) {
+ for (int i = 1; i <= count; i++) {
int aclType = buffer.getInt();
int aclFlag = buffer.getInt();
int aclMask = buffer.getInt();
@@ -745,11 +764,11 @@ public final class SftpHelper {
public static AclEntry buildAclEntry(int aclType, int aclFlag, int aclMask, String aclWho) {
UserPrincipal who = new DefaultGroupPrincipal(aclWho);
return AclEntry.newBuilder()
- .setType(ValidateUtils.checkNotNull(decodeAclEntryType(aclType), "Unknown ACL type: %d", aclType))
- .setFlags(decodeAclFlags(aclFlag))
- .setPermissions(decodeAclMask(aclMask))
- .setPrincipal(who)
- .build();
+ .setType(ValidateUtils.checkNotNull(decodeAclEntryType(aclType), "Unknown ACL type: %d", aclType))
+ .setFlags(decodeAclFlags(aclFlag))
+ .setPermissions(decodeAclMask(aclMask))
+ .setPrincipal(who)
+ .build();
}
/**
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/db6e5b53/sshd-sftp/src/main/java/org/apache/sshd/common/subsystem/sftp/extensions/AclSupportedParser.java
----------------------------------------------------------------------
diff --git a/sshd-sftp/src/main/java/org/apache/sshd/common/subsystem/sftp/extensions/AclSupportedParser.java b/sshd-sftp/src/main/java/org/apache/sshd/common/subsystem/sftp/extensions/AclSupportedParser.java
index a724507..b796ab7 100644
--- a/sshd-sftp/src/main/java/org/apache/sshd/common/subsystem/sftp/extensions/AclSupportedParser.java
+++ b/sshd-sftp/src/main/java/org/apache/sshd/common/subsystem/sftp/extensions/AclSupportedParser.java
@@ -29,9 +29,11 @@ import java.util.Objects;
import java.util.Set;
import java.util.TreeSet;
+import org.apache.sshd.common.SshConstants;
import org.apache.sshd.common.subsystem.sftp.SftpConstants;
import org.apache.sshd.common.subsystem.sftp.extensions.AclSupportedParser.AclCapabilities;
import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.common.util.ValidateUtils;
import org.apache.sshd.common.util.buffer.Buffer;
import org.apache.sshd.common.util.buffer.ByteArrayBuffer;
import org.apache.sshd.common.util.logging.LoggingUtils;
@@ -55,6 +57,9 @@ public class AclSupportedParser extends AbstractParser<AclCapabilities> {
}
public AclCapabilities(int capabilities) {
+ // Protect against malicious or malformed packets
+ ValidateUtils.checkTrue((capabilities >= 0) && (capabilities < SshConstants.SSH_REQUIRED_PAYLOAD_PACKET_LENGTH_SUPPORT),
+ "Illogical ACL capabilities count: %d", capabilities);
this.capabilities = capabilities;
}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/db6e5b53/sshd-sftp/src/main/java/org/apache/sshd/server/subsystem/sftp/SftpSubsystem.java
----------------------------------------------------------------------
diff --git a/sshd-sftp/src/main/java/org/apache/sshd/server/subsystem/sftp/SftpSubsystem.java b/sshd-sftp/src/main/java/org/apache/sshd/server/subsystem/sftp/SftpSubsystem.java
index b17405a..361fd11 100644
--- a/sshd-sftp/src/main/java/org/apache/sshd/server/subsystem/sftp/SftpSubsystem.java
+++ b/sshd-sftp/src/main/java/org/apache/sshd/server/subsystem/sftp/SftpSubsystem.java
@@ -791,7 +791,8 @@ public class SftpSubsystem
}
if (remaining < length) {
- throw new IllegalStateException("Not enough buffer data for writing to " + fh + ": required=" + length + ", available=" + remaining);
+ throw new IllegalStateException("Not enough buffer data for writing to " + fh
+ + ": required=" + length + ", available=" + remaining);
}
SftpEventListener listener = getSftpEventListenerProxy();