Class KexFilter

java.lang.Object
org.apache.sshd.common.filter.IoFilter
org.apache.sshd.common.session.filters.kex.KexFilter
All Implemented Interfaces:
Filter, Owned<FilterContext>

public class KexFilter extends IoFilter
A filter implementing the KEX protocol.

While a KEX is on-going, high-level messages get queued; they will be written once we've sent our SSH_MSG_NEWKEYS. To avoid that the queue grows too much, all channel RemoteWindows are closed during KEX. RemoteWindows listen to KEX state changes, and they report zero during a KEX. Incoming SSH_MSG_CHANNEL_WINDOW_ADJUST before we receive the peer's KEX_INIT have thus no immediate effect. Once KEX is over, the windows will trigger "adjusted" events and will thus open the channels again.

The implementation guards against malicious peers that never send their KEX_INIT but instead keep sending SSH messages that would require a reply. (The replies would get queued since this side is already in KEX, and after some time we'd run out of memory.)

Such incoming messages are:

  • SSH_MSG_PING from the ping@openssh.com extension. This is not implemented in Apache MINA sshd (yet). These messages are dropped on input during KEX in OpenSSH. See CVE-2025-26466 linked below.
  • SSH_MSG_GLOBAL_REQUEST or SSH_MSG_CHANNEL_REQUEST with want-reply = true should send back a success or failure reply that would be queued.
  • SSH_MSG_CHANNEL_OPEN messages should send back success or failure messages, which would be queued. User code can guard against this by limiting the number of concurrently open channels.
  • SSH_MSG_SERVICE_REQUEST messages. This is somewhat unlikely to occur, since normally there are only two such requests in an SSH connection: a first one for user authentication, then a second one to switch to the connection service. There should be no key exchanges running at these times; they're both early on in the protocol. The request for user auth is sent right after the first key exchange. The failure reply to this is SSH_MSG_DISCONNECT, which will not be queued. But the SSH_MSG_SERVICE_ACCEPT would be queued. But the number of services is limited (in normal SSH exactly two: a user authentication service and then a connection service), and our implementation allows only one service to be active. So there will be exactly one SSH_MSG_SERVCIE_ACCEPT queued; further SSH_MSG_SERVICE_REQUESTs will lead to failure replies and disconnection.
  • SSH_MSG_CHANNEL_DATA: these messages must be passed on and handled. LocalWindow needs to listen to the KEX state, too, and not send back SSH_CHANNEL_WINDOW_ADJUST because those would get queued. At some point, the channel window will be zero, and if the broken or malicious client keeps sending data, the channel will be closed forcibly. Otherwise, the local windows will send window adjustments as appropriate once KEX is over.
  • SSH_MSG_CHANNEL_WINDOW_ADJUST: see above. We pass these messages on, but make the adjustment take effect in the RemoteWindow only after KEX. Sending a large number of window adjustments thus does not cause excessive queueing; at worst (if the peer opens its window too far) it may cause trouble at the malicious peer.
  • Unknown messages. We should reply with SSH_MSG_UNIMPLEMENTED, which is a low-level message that will not be queued.
  • As an additional guard against this kind of misbehavior we implement a configurable parameters:

  • MAX_MSGS_BEFORE_KEX_INIT: if we haven't received the peer's KEX_INIT with the next MAX_MSGS_BEFORE_KEX_INIT incoming messages after having sent our own KEX_INIT, we disconnect the session.
  • The setting has rather high default (1000 messages). With this, we will disconnect even if a peer just keeps sending SSH_MSG_IGNORE packets. If a peer doesn't send any messages, the session idle timeout will disconnect the session.

See Also:
  • Constructor Details

  • Method Details

    • isStrictKex

      public boolean isStrictKex()
    • isInitialKexDone

      public boolean isInitialKexDone()
    • getKexState

      public AtomicReference<org.apache.sshd.common.kex.KexState> getKexState()
    • getNegotiated

      public Map<org.apache.sshd.common.kex.KexProposalOption, String> getNegotiated()
    • getClientProposal

      public Map<org.apache.sshd.common.kex.KexProposalOption, String> getClientProposal()
    • getServerProposal

      public Map<org.apache.sshd.common.kex.KexProposalOption, String> getServerProposal()
    • setClientIdent

      public void setClientIdent(String ident)
    • setServerIdent

      public void setServerIdent(String ident)
    • getSessionId

      public byte[] getSessionId()
    • addKexListener

      public void addKexListener(KexListener listener)
    • removeKexListener

      public void removeKexListener(KexListener listener)
    • in

      public InputHandler in()
      Description copied from interface: Filter
      Retrieves the filter's InputHandler.
      Returns:
      the InputHandler or code null if this filter is an output-only filter
    • out

      public OutputHandler out()
      Description copied from interface: Filter
      Retrieves the filter's OutputHandler.
      Returns:
      the OutputHandler or code null if this filter is an input-only filter
    • shutdown

      public void shutdown()
    • startKex

      public KeyExchangeFuture startKex() throws Exception
      Throws:
      Exception