-
-
Notifications
You must be signed in to change notification settings - Fork 932
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Allow the client to initiate the key exchange #972
Conversation
How can I use this code in my application? Because I'm unable to modify ConnectionInfo.cs file because I installed nuget package in my application. |
@Shafqat-Ali You can clone my forked repo, checkout the branch and build your own version of SSH.NET. |
@geoffstewart thanks, man. I appreciate your help |
Seems to be another solution to my pr #841 |
This solution doesnt stop the client from sending the key exchange message again after recieving the key exchange init from the server. This is easily fixed by adding a condition in KeyExchange.Start to prevent duplicate messages. Once tested with this condition added I was succesfully able to connect to a cisco router while initiating a key exchange. e.g. public virtual void Start(Session session, KeyExchangeInitMessage message)
{
Session = session;
// Send key exchange if server initiated
if (!session.ConnectionInfo.InitiateKeyExchange)
{
SendMessage(session.ClientInitMessage);
}
// Determine encryption algorithm
var clientEncryptionAlgorithmName = (from b in session.ConnectionInfo.Encryptions.Keys
from a in message.EncryptionAlgorithmsClientToServer
where a == b
select a).FirstOrDefault();
... |
Interesting. Without your proposed fix, were you unable to connect to a cisco router? In other words, you needed your change in order for the connection to work? |
I was unable to connect to the Cisco Router without the fix. On wireshark it appeared that without the proposed fix the key exchange would be sent from the client twice and then this would trigger the end of the connection which would lead to this being repeatedly retried until timeout |
OK, I'll look at applying the change to this PR. Thanks! |
@geoffstewart Would you mind adding some unit tests for the change? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
StyleCop issue needs to be fixed.
Refer ConnectionInfo
line 88 please.
I think The logic should also be duplicated into Could/should this be done unconditionally? |
@Rob-Hague I will add the As for doing this unconditionally, I implemented it with a condition because I figured it would negatively affect how connections with other servers would behave. I'd have to defer to others on that, though. |
I have run packet captures against several servers using WinSCP (which is based on PuTTY AFAIK) and OpenSSH as clients, and concluded that neither waits for the server KEXINIT message before sending the client KEXINIT. Based on this and my interpretation of the RFC, I don't think there is any reason for the library to wait for the server KEXINIT message, and I think we should enable the client KEXINIT sending unconditionally. However, there is currently a problem (unconditional or not) which is that if we receive a server KEXINIT before having sent the client KEXINIT, it is possible that we will continue the key exchange procedure and send e.g. SSH_MSG_KEX_ECDH_INIT before we have sent the KEXINIT. This is causing errors in the integration tests locally. To fix, I think we need another wait handle in What do you think? |
OK... I had not applied the changes to |
@Rob-Hague I agree that there has to be a better solution than this PR. Using the |
Look I've commented before that I've already solved this problem check out pull request #841 The problem is that the initiation order of fixed in this code. The code adheres to the RFC where Cisco does not. I was watching this thread to see if you were making a more elegant solution but I see you hit the wall |
@WojciechNagorski I think it would just take a couple of changes to this PR, if @geoffstewart is happy to make them (if not, I can try using this PR as a basis). But I first want to think a bit more about it.
Cool. I think we should still initiate the client KEXINIT without waiting for the server, whether or not it fixes the Cisco problem. If it does, then great. If not, then we need something in addition like #841. |
The most elegant solution would be to give the client x seconds before the server initiates. This would keep it kind of in the RFC, without extra parameters. Then add a parameter for server first or client first, for situations where many quick connections are needed. This would be a slightly more complex solution... |
I write "workaround" because no other SSH client provides such a parameter in the configuration depending on the server manufacturer ;) |
Just for the record, as my memory serves me, when SSH.net identifies first the Cisco device will never respond or identify. This causes a race hazard between SSH.net, on whom talks first. SSH.net first means a connection is never made because Cisco will never identify itself. So IMHO it's more of an identification order issue... |
I'm far from an expert, but I'm happy to continue working on this PR to get something that works. So, in the current implementation (not the PR), it looks like SSH.NET sends protocol version exchange and then waits for messages from the server; including KEXINIT. In looking at another client implmentation (OpenSSH), it looks like they do the protocol version exchange and then directly send the kexinit instead of waiting. https://github.com/openssh/openssh-portable/blob/26f3f3bbc69196d908cad6558c8c7dc5beb8d74a/sshconnect.c#L1561. I know it was suggested above to just always send the |
That's my belief, with the caveat that we need to be able to respond to key re-exchange events, which are also SSH_MSG_KEXINIT messages. So, there is some decision necessary at the point of receiving a KEXINIT as to whether we need to send one back: if it is the initial key exchange, then the answer is no (we've already sent) but if it's a key re-exchange, then we do need to send one. |
Hope I'm not being redundant or to chatty here but here's the SSH RFC I've referred to before In 4.2 the connection is explained. Identification is sent by the client and then the KEXINIT. As this issue states it hangs on KEXINIT, I believe it's because the identification order caused the Cisco to stop responding making it hang on the next action. The identification problem with Cisco needs to be solved so that KEXINIT is accepted. I don't think there is an actual key exchange problem, it's just where it gets stuck. my 2 cts |
Pulled 2023.0.0 today, and reintegrated #841 manually. It's still the only way I can get SSH.NET to connect reliably and repeatably to my Cisco router, so I hope a successful PR will evolve from this. |
@likeMyCoffee @expyram Thanks. Let's now focus this PR on the key exchange and continue discussion relating to identification over in #841. In the meantime, if one of you could demonstrate the identification problem with a packet capture, ideally a failure case with SSH.NET and a successful case with a different client, and post it over there (as a screenshot or otherwise) that would be great. |
if (ConnectionInfo.InitiateKeyExchange) | ||
{ | ||
SendMessage(ClientInitMessage); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So to summarise my thoughts here, we can
- make this unconditional
- add
_keyExchangeInProgress = true
before theSendMessage(ClientInitMessage);
call - move it above line 651 (
_ = ThreadAbstraction.ExecuteThreadLongRunning(MessageListener);
). This will ensure we have sent the client init before we continue the key exchange - Add a parameter
bool sendClientInitMessage
toIKeyExchange.Start
which guardsSendMessage(session.ClientInitMessage);
inKeyExchange.Start
- Add a
private bool _initialKeyExchangeCompleted;
(or similar) member toSession
, which is used to decide thesendClientInitMessage
parameter inSession.OnKeyExchangeInitReceived
.
As for testing, if we make it unconditional then we are covered already 🙂
I've had very little time to look at pushing this idea forward, unfortunately. But I'm happy to see @Rob-Hague created #1274 to keep this rolling. |
It's done in #1274 |
The 2023.0.1 version has been released to Nuget: https://www.nuget.org/packages/SSH.NET/2023.0.1 |
I put 2023.0.1 into production today, and the new connection logic is working well with my Cisco routers. Your efforts are greatly appreciated. |
Seems that connecting to some servers just hangs and times out with both server and client waiting on key exchange. I've read of Cisco switches and I've experienced it with AudioCodes SBC, as well.
This PR offers the ability to initiate the key exchange explicitly for these servers that sit and wait.
This PR may solve #767 .