ssh_handle_key_exchange() times out because authentication is also done inside
This is an intermittent problem when running libssh as server, and the client is trilead-ssh2.
The condition to exit ssh_handle_key_exchange() is when session_state is SSH_SESSION_STATE_AUTHENTICATING, normally. But in this abnormal case, the socket buffer that ssh_packet_socket_callback() takes, contains three messages: SSH2_MSG_NEWKEYS, SSH2_MSG_SERVICE_REQUEST and SSH2_MSG_USERAUTH_REQUEST [1].
So we handle SSH2_MSG_NEWKEYS inside ssh_handle_key_exchange() and changes session_state to SSH_SESSION_STATE_AUTHENTICATING (good). But after that, ssh_packet_socket_callback() would trigger callbacks [2] to handle authentication as well (the other two messages), moving session_state to SSH_SESSION_STATE_AUTHENTICATED. All this happens within the same ssh_handle_key_exchange() so the main event loop doesn't get a chance to see _AUTHENTICATING state. By the time it can check it, it's already _AUTHENTICATED. And the function times out eventually.
Should ssh_server_kex_termination() be changed to accept SSH_SESSION_STATE_AUTHENTICATED as exit condition as well? That seems like the easy way out of this. Although to me it still feels wrong that key exchange code tries to do authentication as well.
In this particular case, I think trilead-ssh2 is at fault as well (I'm guessing) because after sending NEWKEYS, it sends both SERVICE_REQUEST and USERAUTH, without waiting for SERVICE_ACCEPT first [1]. I don't know if that's RFC violation. But it's still a good idea for servers to gracefully handle all cases.
Also there's this code inside key exchange
session->session_state = SSH_SESSION_STATE_AUTHENTICATING;
if (session->flags & SSH_SESSION_FLAG_AUTHENTICATED)
session->session_state = SSH_SESSION_STATE_AUTHENTICATED;
I don't know how it can happen, but it looks like if session state is changed to _AUTHENTICATED, we may run into the same problem.
[2] It's this piece of recursive call
if (processed < receivedlen) {
size_t num;
/* Handle a potential packet left in socket buffer */
SSH_LOG(SSH_LOG_PACKET,
"Processing %zu bytes left in socket buffer",
receivedlen-processed);
ptr = ((uint8_t*)data) + processed;
num = ssh_packet_socket_callback(ptr,
receivedlen - processed,
user);
processed += num;
}