-
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()
. -
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()
.
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.
- Support for reading SSH keys from PEM files containing encrypted private keys
RFC 5958, EncryptedPrivateKeyInfo has
been added. Such PEM files start with
-----BEGIN ENCRYPTED PRIVATE KEY-----
. Reading and decrypting keys from such files requires Bouncy Castle to be present. - Support reading SSH keys from PEM files starting with
-----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. - Support reading encrypted private keys in openSSH format that have been encrypted with an AEAD cipher.
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 configurationNumberOfPasswordPrompts
, which is actually the number of authentication attempts. (In keyboard-interactive authentication, there may be several prompts per authentication attempt.) Only interactive authentication attempts usingUserInteraction
count towards the limit. Attempts fulfilled by explicitly provided passwords (viasession.addPasswordIdentity()
orsession.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:
- The default NIO2 back-end has no default connection time-out at all, so the
verify(MY_TIMEOUT)
call would always time-out afterMY_TIMEOUT
had elapsed. - Apache MINA has a default connection time-out of one minute, so even if
MY_TIMEOUT
was larger, the time-out would still occur after one minute. - Netty has a default connection time-out of 30 seconds.
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.