From 60069be0da155ac7e9f629d528d8d2ce8e84c8d4 Mon Sep 17 00:00:00 2001 From: MrCoffee Date: Tue, 29 Jun 2021 13:49:32 +0200 Subject: [PATCH 1/8] Cisco connection issue fix Certain Cisco devices do not adhere to RFC4342 and do not reply if the client identifies first. Since identifcation can be in random order it will give random connection issues because the SSH_MSG_KEXINIT will not be sent if the client is faster. Since SSH.Net is not at fault and compatibility with Cisco (and possibly other) devices is something that can easily be supported I've written this modification. Added LazyIdentification to the ConnectionInfo object to allow late identification in ProtocolVersionExchange. Overloaded 'Start' function to keep the original functionality and tests intact. Highly likely fixes issues #752, #778, #469 and might help with #798, #767, #807 --- .../Connection/IProtocolVersionExchange.cs | 12 ++++++++ .../Connection/ProtocolVersionExchange.cs | 30 +++++++++++++++++-- src/Renci.SshNet/ConnectionInfo.cs | 10 +++++++ src/Renci.SshNet/Session.cs | 2 +- 4 files changed, 50 insertions(+), 4 deletions(-) diff --git a/src/Renci.SshNet/Connection/IProtocolVersionExchange.cs b/src/Renci.SshNet/Connection/IProtocolVersionExchange.cs index 77bcfbd33..3e1a796c5 100644 --- a/src/Renci.SshNet/Connection/IProtocolVersionExchange.cs +++ b/src/Renci.SshNet/Connection/IProtocolVersionExchange.cs @@ -18,5 +18,17 @@ internal interface IProtocolVersionExchange /// The SSH identification of the server. /// SshIdentification Start(string clientVersion, Socket socket, TimeSpan timeout); + + /// + /// Performs the SSH protocol version exchange. + /// + /// The identification string of the SSH client. + /// A connected to the server. + /// The maximum time to wait for the server to respond. + /// Allow server to identify itself first. + /// + /// The SSH identification of the server. + /// + SshIdentification Start(string clientVersion, Socket socket, TimeSpan timeout, bool lazyIdentification); } } diff --git a/src/Renci.SshNet/Connection/ProtocolVersionExchange.cs b/src/Renci.SshNet/Connection/ProtocolVersionExchange.cs index 1d529a6d1..73d4fd468 100644 --- a/src/Renci.SshNet/Connection/ProtocolVersionExchange.cs +++ b/src/Renci.SshNet/Connection/ProtocolVersionExchange.cs @@ -37,9 +37,27 @@ internal class ProtocolVersionExchange : IProtocolVersionExchange /// public SshIdentification Start(string clientVersion, Socket socket, TimeSpan timeout) { - // Immediately send the identification string since the spec states both sides MUST send an identification string - // when the connection has been established - SocketAbstraction.Send(socket, Encoding.UTF8.GetBytes(clientVersion + "\x0D\x0A")); + return Start(clientVersion, socket, timeout, false); + } + + /// + /// Performs the SSH protocol version exchange. + /// + /// The identification string of the SSH client. + /// A connected to the server. + /// The maximum time to wait for the server to respond. + /// Allow server to identify itself first. + /// + /// The SSH identification of the server. + /// + public SshIdentification Start(string clientVersion, Socket socket, TimeSpan timeout, bool lazyIdentification) + { + if (!lazyIdentification) + { + // Immediately send the identification string since the spec states both sides MUST send an identification string + // when the connection has been established + SocketAbstraction.Send(socket, Encoding.UTF8.GetBytes(clientVersion + "\x0D\x0A")); + } var bytesReceived = new List(); @@ -71,6 +89,12 @@ public SshIdentification Start(string clientVersion, Socket socket, TimeSpan tim var identificationMatch = ServerVersionRe.Match(line); if (identificationMatch.Success) { + if (lazyIdentification) + { + // Send identification only after server identification has been validated. + SocketAbstraction.Send(socket, Encoding.UTF8.GetBytes(clientVersion + "\x0D\x0A")); + } + return new SshIdentification(GetGroupValue(identificationMatch, "protoversion"), GetGroupValue(identificationMatch, "softwareversion"), GetGroupValue(identificationMatch, "comments")); diff --git a/src/Renci.SshNet/ConnectionInfo.cs b/src/Renci.SshNet/ConnectionInfo.cs index af8e1ca5c..ebb0dc3ac 100644 --- a/src/Renci.SshNet/ConnectionInfo.cs +++ b/src/Renci.SshNet/ConnectionInfo.cs @@ -183,6 +183,14 @@ public class ConnectionInfo : IConnectionInfoInternal /// public int MaxSessions { get; set; } + /// + /// Gets or sets if the client should identify itself later. + /// + /// + /// false, the default for strict RFC4253 compliance where both sides identify at the same time. true if the client should wait for the server identification. + /// + public bool LazyIdentification { get; set; } + /// /// Occurs when authentication banner is sent by the server. /// @@ -432,6 +440,8 @@ public ConnectionInfo(string host, int port, string username, ProxyTypes proxyTy ProxyPassword = proxyPassword; AuthenticationMethods = authenticationMethods; + + LazyIdentification = false; } /// diff --git a/src/Renci.SshNet/Session.cs b/src/Renci.SshNet/Session.cs index 0748b8dac..d7eb69369 100644 --- a/src/Renci.SshNet/Session.cs +++ b/src/Renci.SshNet/Session.cs @@ -588,7 +588,7 @@ public void Connect() .Connect(ConnectionInfo); var serverIdentification = _serviceFactory.CreateProtocolVersionExchange() - .Start(ClientVersion, _socket, ConnectionInfo.Timeout); + .Start(ClientVersion, _socket, ConnectionInfo.Timeout, ConnectionInfo.LazyIdentification); // Set connection versions ServerVersion = ConnectionInfo.ServerVersion = serverIdentification.ToString(); From 18b95f8a2a4644a56e50e2fee006be360fed1b5b Mon Sep 17 00:00:00 2001 From: likeMyCoffee Date: Mon, 27 Mar 2023 23:35:01 +0200 Subject: [PATCH 2/8] Update IProtocolVersionExchange.cs Default parameter instead of overloading --- src/Renci.SshNet/Connection/IProtocolVersionExchange.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Renci.SshNet/Connection/IProtocolVersionExchange.cs b/src/Renci.SshNet/Connection/IProtocolVersionExchange.cs index 68c3c334c..572c0020a 100644 --- a/src/Renci.SshNet/Connection/IProtocolVersionExchange.cs +++ b/src/Renci.SshNet/Connection/IProtocolVersionExchange.cs @@ -17,7 +17,7 @@ internal interface IProtocolVersionExchange /// /// The SSH identification of the server. /// - SshIdentification Start(string clientVersion, Socket socket, TimeSpan timeout); + SshIdentification Start(string clientVersion, Socket socket, TimeSpan timeout, bool lazyIdentification = false); /// @@ -30,7 +30,7 @@ internal interface IProtocolVersionExchange /// /// The SSH identification of the server. /// - SshIdentification Start(string clientVersion, Socket socket, TimeSpan timeout, bool lazyIdentification); + // SshIdentification Start(string clientVersion, Socket socket, TimeSpan timeout, bool lazyIdentification); #if FEATURE_TAP System.Threading.Tasks.Task StartAsync(string clientVersion, Socket socket, System.Threading.CancellationToken cancellationToken); From 2407b0e136d00fe2d4ad17f3f359b727e12acbee Mon Sep 17 00:00:00 2001 From: likeMyCoffee Date: Mon, 27 Mar 2023 23:37:52 +0200 Subject: [PATCH 3/8] Update ProtocolVersionExchange.cs --- src/Renci.SshNet/Connection/ProtocolVersionExchange.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Renci.SshNet/Connection/ProtocolVersionExchange.cs b/src/Renci.SshNet/Connection/ProtocolVersionExchange.cs index 27fafd6be..a3ef062b7 100644 --- a/src/Renci.SshNet/Connection/ProtocolVersionExchange.cs +++ b/src/Renci.SshNet/Connection/ProtocolVersionExchange.cs @@ -39,10 +39,10 @@ internal class ProtocolVersionExchange : IProtocolVersionExchange /// /// The SSH identification of the server. /// - public SshIdentification Start(string clientVersion, Socket socket, TimeSpan timeout) - { - return Start(clientVersion, socket, timeout, false); - } + //public SshIdentification Start(string clientVersion, Socket socket, TimeSpan timeout) + //{ + // return Start(clientVersion, socket, timeout, false); + //} /// /// Performs the SSH protocol version exchange. @@ -54,7 +54,7 @@ public SshIdentification Start(string clientVersion, Socket socket, TimeSpan tim /// /// The SSH identification of the server. /// - public SshIdentification Start(string clientVersion, Socket socket, TimeSpan timeout, bool lazyIdentification) + public SshIdentification Start(string clientVersion, Socket socket, TimeSpan timeout, bool lazyIdentification = false) { if (!lazyIdentification) { From fea71d99c27c770b3dedcc8e4e59a74ff1de6fc3 Mon Sep 17 00:00:00 2001 From: likeMyCoffee Date: Mon, 27 Mar 2023 23:45:44 +0200 Subject: [PATCH 4/8] Update IProtocolVersionExchange.cs --- .../Connection/IProtocolVersionExchange.cs | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/src/Renci.SshNet/Connection/IProtocolVersionExchange.cs b/src/Renci.SshNet/Connection/IProtocolVersionExchange.cs index 572c0020a..001a38608 100644 --- a/src/Renci.SshNet/Connection/IProtocolVersionExchange.cs +++ b/src/Renci.SshNet/Connection/IProtocolVersionExchange.cs @@ -19,19 +19,6 @@ internal interface IProtocolVersionExchange /// SshIdentification Start(string clientVersion, Socket socket, TimeSpan timeout, bool lazyIdentification = false); - - /// - /// Performs the SSH protocol version exchange. - /// - /// The identification string of the SSH client. - /// A connected to the server. - /// The maximum time to wait for the server to respond. - /// Allow server to identify itself first. - /// - /// The SSH identification of the server. - /// - // SshIdentification Start(string clientVersion, Socket socket, TimeSpan timeout, bool lazyIdentification); - #if FEATURE_TAP System.Threading.Tasks.Task StartAsync(string clientVersion, Socket socket, System.Threading.CancellationToken cancellationToken); #endif From e8a7583d65b38875962038e9810a9ab89aa237fa Mon Sep 17 00:00:00 2001 From: likeMyCoffee Date: Mon, 27 Mar 2023 23:46:44 +0200 Subject: [PATCH 5/8] Update ProtocolVersionExchange.cs --- .../Connection/ProtocolVersionExchange.cs | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/src/Renci.SshNet/Connection/ProtocolVersionExchange.cs b/src/Renci.SshNet/Connection/ProtocolVersionExchange.cs index a3ef062b7..e5b30d484 100644 --- a/src/Renci.SshNet/Connection/ProtocolVersionExchange.cs +++ b/src/Renci.SshNet/Connection/ProtocolVersionExchange.cs @@ -30,20 +30,6 @@ internal class ProtocolVersionExchange : IProtocolVersionExchange private static readonly Regex ServerVersionRe = new Regex("^SSH-(?[^-]+)-(?.+?)([ ](?.+))?$"); #endif - /// - /// Performs the SSH protocol version exchange. - /// - /// The identification string of the SSH client. - /// A connected to the server. - /// The maximum time to wait for the server to respond. - /// - /// The SSH identification of the server. - /// - //public SshIdentification Start(string clientVersion, Socket socket, TimeSpan timeout) - //{ - // return Start(clientVersion, socket, timeout, false); - //} - /// /// Performs the SSH protocol version exchange. /// From a26ce0c87c825284e34d65bd39a03182f56c13bb Mon Sep 17 00:00:00 2001 From: likeMyCoffee Date: Mon, 27 Mar 2023 23:49:19 +0200 Subject: [PATCH 6/8] Update IProtocolVersionExchange.cs Fixing XML --- src/Renci.SshNet/Connection/IProtocolVersionExchange.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Renci.SshNet/Connection/IProtocolVersionExchange.cs b/src/Renci.SshNet/Connection/IProtocolVersionExchange.cs index 001a38608..bfb75ae2b 100644 --- a/src/Renci.SshNet/Connection/IProtocolVersionExchange.cs +++ b/src/Renci.SshNet/Connection/IProtocolVersionExchange.cs @@ -14,6 +14,7 @@ internal interface IProtocolVersionExchange /// The identification string of the SSH client. /// A connected to the server. /// The maximum time to wait for the server to respond. + /// Allow client to identify first. /// /// The SSH identification of the server. /// From 32ebe63d834a0e1e422a67c77bc855512401624e Mon Sep 17 00:00:00 2001 From: likeMyCoffee Date: Mon, 27 Mar 2023 23:53:35 +0200 Subject: [PATCH 7/8] Update IProtocolVersionExchange.cs Can't delete commits so reverted back manually. Optional parameters not possible --- .../Connection/IProtocolVersionExchange.cs | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/src/Renci.SshNet/Connection/IProtocolVersionExchange.cs b/src/Renci.SshNet/Connection/IProtocolVersionExchange.cs index bfb75ae2b..68c3c334c 100644 --- a/src/Renci.SshNet/Connection/IProtocolVersionExchange.cs +++ b/src/Renci.SshNet/Connection/IProtocolVersionExchange.cs @@ -14,11 +14,23 @@ internal interface IProtocolVersionExchange /// The identification string of the SSH client. /// A connected to the server. /// The maximum time to wait for the server to respond. - /// Allow client to identify first. /// /// The SSH identification of the server. /// - SshIdentification Start(string clientVersion, Socket socket, TimeSpan timeout, bool lazyIdentification = false); + SshIdentification Start(string clientVersion, Socket socket, TimeSpan timeout); + + + /// + /// Performs the SSH protocol version exchange. + /// + /// The identification string of the SSH client. + /// A connected to the server. + /// The maximum time to wait for the server to respond. + /// Allow server to identify itself first. + /// + /// The SSH identification of the server. + /// + SshIdentification Start(string clientVersion, Socket socket, TimeSpan timeout, bool lazyIdentification); #if FEATURE_TAP System.Threading.Tasks.Task StartAsync(string clientVersion, Socket socket, System.Threading.CancellationToken cancellationToken); From be80b4f7afefbfd7e650d4d73ca8d284ba3fa454 Mon Sep 17 00:00:00 2001 From: likeMyCoffee Date: Mon, 27 Mar 2023 23:55:59 +0200 Subject: [PATCH 8/8] Update ProtocolVersionExchange.cs Optional parameters not possible. Reverted back using commit. --- .../Connection/ProtocolVersionExchange.cs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/Renci.SshNet/Connection/ProtocolVersionExchange.cs b/src/Renci.SshNet/Connection/ProtocolVersionExchange.cs index e5b30d484..5f41f50c2 100644 --- a/src/Renci.SshNet/Connection/ProtocolVersionExchange.cs +++ b/src/Renci.SshNet/Connection/ProtocolVersionExchange.cs @@ -30,6 +30,20 @@ internal class ProtocolVersionExchange : IProtocolVersionExchange private static readonly Regex ServerVersionRe = new Regex("^SSH-(?[^-]+)-(?.+?)([ ](?.+))?$"); #endif + /// + /// Performs the SSH protocol version exchange. + /// + /// The identification string of the SSH client. + /// A connected to the server. + /// The maximum time to wait for the server to respond. + /// + /// The SSH identification of the server. + /// + public SshIdentification Start(string clientVersion, Socket socket, TimeSpan timeout) + { + return Start(clientVersion, socket, timeout, false); + } + /// /// Performs the SSH protocol version exchange. ///