GH-268 (Regression in 2.9.0) Heartbeat should throw an exception if no reply arrives within the timeout.
GH-275 SFTP: be more lenient when reading SSH_FXP_STATUS
replies.
GH-281 Use OpenSSH first-match semantics for processing HostConfigEntries.
GH-282 Correct setting file permissions on newly written host key files on Windows.
GH-283 Fix handling of CoreModuleProperties.PASSWORD_PROMPTS
.
GH-285 Fix compilation failure on Java 19.
GH-293 Handle SFTP buffer sizes larger than the server limit better.
GH-294 Fix memory leak in SftpFileSystemProvider
.
GH-297 Auto-configure file password provider for reading encrypted SSH keys.
GH-298 Server side heartbeat not working.
GH-300 Read the channel id in SSH_MSG_CHANNEL_OPEN_CONFIRMATION
as unsigned int.
GH-313 Log exceptions in the SFTP subsystem before sending a failure status reply.
GH-322 Add basic Android O/S awareness.
GH-325 SftpFileSystemProvider
: fix deletions of symlinks through Files.delete()
.
GH-364 AbstractAgentClient
: respect flags for RSA signature requests.
SSHD-1295 Fix cancellation of futures and add options to cancel futures on time-outs.
SSHD-1315 Do not log sensitive data at log level TRACE
.
SSHD-1316 Possible OOM in ChannelPipedInputStream
(fix channel window).
SSHD-1319 Use position in SftpRemotePathChannel.transferFrom()
.
SSHD-1324 SFTP: Rooted file system can leak information.
Apache MINA sshd is an asynchronous framework: for long-running operations, and in particular operations involving network communication, the API returns a future that client code can use to wait until the operation is complete. Waiting on a future is usually done with a future.verify()
or future.verify(timeout)
call. Futures can also be canceled.
Previous versions did not implement cancellation correctly: while the future object itself was marked as “canceled”, the underlying operation was not. The same problem also existed for time-outs: when future.verify(timeout)
timed out, the underlying operation still continued to run asynchronously. This could lead to problems because if the underlying operation eventually succeeded, application code would be completely unaware. For instance in
ClientSession session = client.connect(user, host, port).verify(timeout).getSession();
the application might get a time-out, but the underlying connect operation would continue to run, might succeed eventually, and then there would in fact be a ClientSession
(and thus also a network connection, and a socket used up). But the application had no way to access that session to shut it down. The net effect was a socket leak.
In this version, this has been corrected. By default, the future is canceled when a time-out occurs, and future.cancel()
is propagated to the underlying operation and cancels it.
Canceling an operation itself may not be possible immediately. For instance, an authentication attempt is a message exchange with the server. If the authentication request has already been sent when cancellation is requested, the sending of that request cannot be undone. The authentication can only be cancelled after the reply from the server has been received, and if that reply is an authentication success, cancellation isn't even possible anymore. Or consider requesting a port forwarding: that, too, is a request-reply message exchange. Once the request has been sent, there are two cases: if the server replies with a failure message, the port forwarding failed and since there is nothing to cancel, cancellation is not possible. If the reply indicates the tunnel was established, but future.cancel()
had already been called, we have two options: either we shut down the just established tunnel again and say cancellation succeeded, or we say cancellation failed and report a successfully established tunnel. Because cancellations may be caused by time-outs, Apache MINA sshd chooses the first option and shuts down the tunnel again. Otherwise an application might get a time-out but still be left with an established tunnel.
Cancellation is only possible while the operation has not completed yet. If a future is already done, it cannot be canceled anymore.
The cancel()
operation on a future is thus a request to cancel the operation; it may or may not result in actually cancelling the operation. cancel()
itself therefore returns a CancelFuture
that client code can use to wait for the request having been handled, and then learn whether the operation was indeed canceled.
Calls to verify()
throw an SshException
with a java.util.concurrent.CancellationException
as cause if cancelled asynchronously via the cancel()
method.
Application code can control the behavior on time-outs. The verify()
method takes besides a time-out duration newly also a number of CancelOption
s as parameters.
There are three possible values to cancel on a time-out, to cancel on an interruption, or not to cancel at all when either occurs. For backwards compatibility, the default behavior of AuthFuture
and of OpenFuture
is unchanged: to cancel on a time-out, client code must pass the CancelOption.CANCEL_ON_TIMEOUT
flag.
The default behavior of ConnectFuture
has been changed: by default, it does cancel the connection attempt if a time-out occurs. To avoid this, client code would have to pass the CancelOption.NO_CANCELLATION
flag expressly. This change in behavior was done to avoid socket leaks, and it was deemed acceptable since a difference in behavior could only occur if existing client code called AuthFuture.verify()
in different threads on the same future instance, or sequentially on the same future instance again after the first time-out. Both cases are highly unlikely to occur in existing client code. If existing code needs this behavior, it needs to be adapted to pass CancelOption
s as may be appropriate for the precise use case.
-----BEGIN ENCRYPTED PRIVATE KEY-----
. Reading and decrypting keys from such files requires Bouncy Castle to be present.-----BEGIN ED25519 PRIVATE KEY-----
. Some OpenSSL versions could produce such files when the user specified “traditional” PEM output. (Encrypted keys written using RFC 1421 encryption.) Modern OpenSSL refuses to create such PEM files; it always uses PKCS#8 (RFC 5958) style PEM files for EdDSA keys.CoreModuleProperties.PASSWORD_PROMPTS
is now also used for password authentication. Previous versions used it only for keyboard-interactive authentication. The semantics has been clarified to be the equivalent of the OpenSSH configuration NumberOfPasswordPrompts
, which is actually the number of authentication attempts. (In keyboard-interactive authentication, there may be several prompts per authentication attempt.) Only interactive authentication attempts using UserInteraction
count towards the limit. Attempts fulfilled by explicitly provided passwords (via session.addPasswordIdentity()
or session.setPasswordIdentityProvider()
) are not counted. The default value of the property is unchanged and is 3, as in OpenSSH. The limit is applied independently for both authentication mechanisms: with the default setting, there can be three keyboard-interactive authentication attempts, plus three more password authentication attempts if both methods are configured and applicable.Connection time-outs are normally handled in Apache MINA SSHD at the application level by passing a time-out to ConnectFuture.verify()
:
ClientSession session = client.connect(user, host, port).verify(MY_TIMEOUT).getSession();
However, the actual I/O library used might have its own connection time-out. With large time-outs, the behavior depended on the actual implementation of the I/O back-end used:
verify(MY_TIMEOUT)
call would always time-out after MY_TIMEOUT
had elapsed.MY_TIMEOUT
was larger, the time-out would still occur after one minute.In this version, a new property CoreModuleProperties.IO_CONNECTION_TIMEOUT
can be set to control this I/O connection time-out. It can be set on an SshClient
or SshServer
; if set (and > 0), the I/O back-end is configured to use that value as its I/O connection time-out, and ConnectFuture.verify(MY_TIMEOUT)
will always time-out at Math.min(CoreModuleProperties.IO_CONNECTION_TIMEOUT, MY_TIMEOUT)
. The property is also effective for the NIO2 back-end; the default value is 1 minute.
verify()
throws an SshException
if it fails or times out. The cause of that exception is a java.net.ConnectException
if the I/O connection time-out expired, and a java.util.concurrent.TimeoutException
if the application time-out expired. (And if the future was canceled explicitly before any time-out was reached, the cause is a java.util.concurrent.CancellationException
; see above.)
The new CancelOption
s discussed above apply only if the application time-out expires. If the connection attempt times out at I/O level, it is the responsibility of the I/O library to ensure no resources such as sockets are consumed, and there is no SSH session created either.