diff options
author | Aris Adamantiadis <aris@0xbadc0de.be> | 2009-12-13 18:20:05 +0100 |
---|---|---|
committer | Aris Adamantiadis <aris@0xbadc0de.be> | 2009-12-13 18:20:05 +0100 |
commit | c92f54102eebe024c8975a96947b35a5300be5d4 (patch) | |
tree | 9d2d60f1e0fd38f2f237d569466906a4cde5dd88 | |
parent | 964d5f88cceca5134ad03822ad1b1c47669f7d5c (diff) | |
download | libssh-c92f54102eebe024c8975a96947b35a5300be5d4.tar.gz libssh-c92f54102eebe024c8975a96947b35a5300be5d4.tar.xz libssh-c92f54102eebe024c8975a96947b35a5300be5d4.zip |
Key exchange currently done asynchronously
Fixed an important bug in packet buffering (two packets received in the same recv would'nt be processed correctly)
-rw-r--r-- | include/libssh/priv.h | 2 | ||||
-rw-r--r-- | libssh/client.c | 280 | ||||
-rw-r--r-- | libssh/packet.c | 14 |
3 files changed, 160 insertions, 136 deletions
diff --git a/include/libssh/priv.h b/include/libssh/priv.h index 19044773..1b751842 100644 --- a/include/libssh/priv.h +++ b/include/libssh/priv.h @@ -121,6 +121,8 @@ SSH_PACKET_CALLBACK(ssh_packet_ignore_callback); int ssh_send_banner(ssh_session session, int is_server); void ssh_connection_callback(ssh_session session); +SSH_PACKET_CALLBACK(ssh_packet_dh_reply); +SSH_PACKET_CALLBACK(ssh_packet_newkeys); /* config.c */ int ssh_config_parse_file(ssh_session session, const char *filename); diff --git a/libssh/client.c b/libssh/client.c index 82eb0c2a..92fe215c 100644 --- a/libssh/client.c +++ b/libssh/client.c @@ -248,13 +248,144 @@ int ssh_send_banner(ssh_session session, int server) { enum ssh_dh_state_e { DH_STATE_INIT, - DH_STATE_INIT_TO_SEND, DH_STATE_INIT_SENT, - DH_STATE_NEWKEYS_TO_SEND, DH_STATE_NEWKEYS_SENT, DH_STATE_FINISHED }; +SSH_PACKET_CALLBACK(ssh_packet_dh_reply){ + ssh_string f = NULL; + ssh_string pubkey = NULL; + ssh_string signature = NULL; + (void)type; + (void)user; + ssh_log(session,SSH_LOG_PROTOCOL,"Received SSH_KEXDH_REPLY"); + if(session->session_state!= SSH_SESSION_STATE_DH && + session->dh_handshake_state != DH_STATE_INIT_SENT){ + ssh_set_error(session,SSH_FATAL,"ssh_packet_dh_reply called in wrong state : %d:%d", + session->session_state,session->dh_handshake_state); + goto error; + } + + pubkey = buffer_get_ssh_string(packet); + if (pubkey == NULL){ + ssh_set_error(session,SSH_FATAL, "No public key in packet"); + goto error; + } + dh_import_pubkey(session, pubkey); + + f = buffer_get_ssh_string(packet); + if (f == NULL) { + ssh_set_error(session,SSH_FATAL, "No F number in packet"); + goto error; + } + if (dh_import_f(session, f) < 0) { + ssh_set_error(session, SSH_FATAL, "Cannot import f number"); + goto error; + } + string_burn(f); + string_free(f); + f=NULL; + signature = buffer_get_ssh_string(packet); + if (signature == NULL) { + ssh_set_error(session, SSH_FATAL, "No signature in packet"); + goto error; + } + session->dh_server_signature = signature; + if (dh_build_k(session) < 0) { + ssh_set_error(session, SSH_FATAL, "Cannot build k number"); + goto error; + } + + /* Send the MSG_NEWKEYS */ + if (buffer_add_u8(session->out_buffer, SSH2_MSG_NEWKEYS) < 0) { + goto error; + } + + packet_send(session); + ssh_log(session, SSH_LOG_PROTOCOL, "SSH_MSG_NEWKEYS sent"); + + session->dh_handshake_state = DH_STATE_NEWKEYS_SENT; + return SSH_PACKET_USED; +error: + session->session_state=SSH_SESSION_STATE_ERROR; + return SSH_PACKET_USED; +} + +SSH_PACKET_CALLBACK(ssh_packet_newkeys){ + ssh_string signature = NULL; + int rc; + (void)packet; + (void)user; + (void)type; + ssh_log(session, SSH_LOG_PROTOCOL, "Received SSH_MSG_NEWKEYS"); + if(session->session_state!= SSH_SESSION_STATE_DH && + session->dh_handshake_state != DH_STATE_NEWKEYS_SENT){ + ssh_set_error(session,SSH_FATAL,"ssh_packet_newkeys called in wrong state : %d:%d", + session->session_state,session->dh_handshake_state); + goto error; + } + rc = make_sessionid(session); + if (rc != SSH_OK) { + goto error; + } + + /* + * Set the cryptographic functions for the next crypto + * (it is needed for generate_session_keys for key lenghts) + */ + if (crypt_set_algorithms(session)) { + goto error; + } + + if (generate_session_keys(session) < 0) { + goto error; + } + + /* Verify the host's signature. FIXME do it sooner */ + signature = session->dh_server_signature; + session->dh_server_signature = NULL; + if (signature_verify(session, signature)) { + goto error; + } + + /* forget it for now ... */ + string_burn(signature); + string_free(signature); + signature=NULL; + /* + * Once we got SSH2_MSG_NEWKEYS we can switch next_crypto and + * current_crypto + */ + if (session->current_crypto) { + crypto_free(session->current_crypto); + session->current_crypto=NULL; + } + + /* FIXME later, include a function to change keys */ + session->current_crypto = session->next_crypto; + + session->next_crypto = crypto_new(); + if (session->next_crypto == NULL) { + ssh_set_error_oom(session); + goto error; + } + + session->dh_handshake_state = DH_STATE_FINISHED; + ssh_connection_callback(session); + return SSH_PACKET_USED; +error: + session->session_state=SSH_SESSION_STATE_ERROR; + return SSH_PACKET_USED; +} + +/** @internal + * @brief launches the DH handshake state machine + * @param session session handle + * @returns SSH_OK or SSH_ERROR + * @warning this function returning is no proof that DH handshake is + * completed + */ static int dh_handshake(ssh_session session) { ssh_string e = NULL; ssh_string f = NULL; @@ -294,143 +425,25 @@ static int dh_handshake(ssh_session session) { goto error; } - session->dh_handshake_state = DH_STATE_INIT_TO_SEND; - case DH_STATE_INIT_TO_SEND: - rc = packet_flush(session, 0); - if (rc != SSH_OK) { - goto error; - } session->dh_handshake_state = DH_STATE_INIT_SENT; case DH_STATE_INIT_SENT: - rc = packet_wait(session, SSH2_MSG_KEXDH_REPLY, 1); - if (rc != SSH_OK) { - goto error; - } - - pubkey = buffer_get_ssh_string(session->in_buffer); - if (pubkey == NULL){ - ssh_set_error(session,SSH_FATAL, "No public key in packet"); - rc = SSH_ERROR; - goto error; - } - dh_import_pubkey(session, pubkey); - - f = buffer_get_ssh_string(session->in_buffer); - if (f == NULL) { - ssh_set_error(session,SSH_FATAL, "No F number in packet"); - rc = SSH_ERROR; - goto error; - } - if (dh_import_f(session, f) < 0) { - ssh_set_error(session, SSH_FATAL, "Cannot import f number"); - rc = SSH_ERROR; - goto error; - } - string_burn(f); - string_free(f); - f=NULL; - signature = buffer_get_ssh_string(session->in_buffer); - if (signature == NULL) { - ssh_set_error(session, SSH_FATAL, "No signature in packet"); - rc = SSH_ERROR; - goto error; - } - session->dh_server_signature = signature; - if (dh_build_k(session) < 0) { - ssh_set_error(session, SSH_FATAL, "Cannot build k number"); - rc = SSH_ERROR; - goto error; - } - - /* Send the MSG_NEWKEYS */ - if (buffer_add_u8(session->out_buffer, SSH2_MSG_NEWKEYS) < 0) { - rc = SSH_ERROR; - goto error; - } - - rc = packet_send(session); - if (rc == SSH_ERROR) { - goto error; - } - - session->dh_handshake_state = DH_STATE_NEWKEYS_TO_SEND; - case DH_STATE_NEWKEYS_TO_SEND: - rc = packet_flush(session, 0); - if (rc != SSH_OK) { - goto error; - } - ssh_log(session, SSH_LOG_RARE, "SSH_MSG_NEWKEYS sent\n"); - - session->dh_handshake_state = DH_STATE_NEWKEYS_SENT; + /* wait until ssh_packet_dh_reply is called */ + break; case DH_STATE_NEWKEYS_SENT: - rc = packet_wait(session, SSH2_MSG_NEWKEYS, 1); - if (rc != SSH_OK) { - goto error; - } - ssh_log(session, SSH_LOG_RARE, "Got SSH_MSG_NEWKEYS\n"); - - rc = make_sessionid(session); - if (rc != SSH_OK) { - goto error; - } - - /* - * Set the cryptographic functions for the next crypto - * (it is needed for generate_session_keys for key lenghts) - */ - if (crypt_set_algorithms(session)) { - rc = SSH_ERROR; - goto error; - } - - if (generate_session_keys(session) < 0) { - rc = SSH_ERROR; - goto error; - } - - /* Verify the host's signature. FIXME do it sooner */ - signature = session->dh_server_signature; - session->dh_server_signature = NULL; - if (signature_verify(session, signature)) { - rc = SSH_ERROR; - goto error; - } - - /* forget it for now ... */ - string_burn(signature); - string_free(signature); - signature=NULL; - /* - * Once we got SSH2_MSG_NEWKEYS we can switch next_crypto and - * current_crypto - */ - if (session->current_crypto) { - crypto_free(session->current_crypto); - session->current_crypto=NULL; - } - - /* FIXME later, include a function to change keys */ - session->current_crypto = session->next_crypto; - - session->next_crypto = crypto_new(); - if (session->next_crypto == NULL) { - rc = SSH_ERROR; - goto error; - } - - session->dh_handshake_state = DH_STATE_FINISHED; - - leave_function(); + /* wait until ssh_packet_newkeys is calles */ + break; + case DH_STATE_FINISHED: + leave_function(); return SSH_OK; default: ssh_set_error(session, SSH_FATAL, "Invalid state in dh_handshake(): %d", session->dh_handshake_state); - leave_function(); return SSH_ERROR; } - /* not reached */ + leave_function(); + return SSH_AGAIN; error: if(e != NULL){ string_burn(e); @@ -565,6 +578,7 @@ void ssh_connection_callback(ssh_session session){ session->session_state=SSH_SESSION_STATE_AUTHENTICATING; break; } + break; case SSH_SESSION_STATE_KEXINIT_RECEIVED: set_status(session,0.6); ssh_list_kex(session, &session->server_kex); @@ -576,15 +590,13 @@ void ssh_connection_callback(ssh_session session){ } set_status(session,0.8); session->session_state=SSH_SESSION_STATE_DH; - break; - case SSH_SESSION_STATE_DH: - if (dh_handshake(session) < 0) { + if (dh_handshake(session) == SSH_ERROR) { goto error; } + case SSH_SESSION_STATE_DH: if(session->dh_handshake_state==DH_STATE_FINISHED){ set_status(session,1.0); session->connected = 1; - break; session->session_state=SSH_SESSION_STATE_AUTHENTICATING; } break; @@ -661,7 +673,9 @@ int ssh_connect(ssh_session session) { ssh_log(session,SSH_LOG_PACKET,"ssh_connect: Actual state : %d",session->session_state); } leave_function(); - return 0; + if(session->session_state == SSH_SESSION_STATE_ERROR) + return SSH_ERROR; + return SSH_OK; } /** diff --git a/libssh/packet.c b/libssh/packet.c index db9f9fad..6a3e0aef 100644 --- a/libssh/packet.c +++ b/libssh/packet.c @@ -57,10 +57,10 @@ ssh_packet_callback default_packet_handlers[]= { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, // 7-19 ssh_packet_kexinit, //#define SSH2_MSG_KEXINIT 20 - NULL, //#define SSH2_MSG_NEWKEYS 21 + ssh_packet_newkeys, //#define SSH2_MSG_NEWKEYS 21 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, //22-29 NULL, //#define SSH2_MSG_KEXDH_INIT 30 SSH2_MSG_KEX_DH_GEX_REQUEST_OLD 30 - NULL, // #define SSH2_MSG_KEXDH_REPLY 31 SSH2_MSG_KEX_DH_GEX_GROUP 31 + ssh_packet_dh_reply, // #define SSH2_MSG_KEXDH_REPLY 31 SSH2_MSG_KEX_DH_GEX_GROUP 31 NULL, //#define SSH2_MSG_KEX_DH_GEX_INIT 32 NULL, //#define SSH2_MSG_KEX_DH_GEX_REPLY 33 NULL, //#define SSH2_MSG_KEX_DH_GEX_REQUEST 34 @@ -260,10 +260,18 @@ int ssh_packet_socket_callback(const void *data, size_t receivedlen, void *user) /* execute callbacks */ ssh_packet_process(session, session->in_packet.type); session->packet_state = PACKET_STATE_INIT; + if(processed < receivedlen){ + /* Handle a potential packet left in socket buffer */ + ssh_log(session,SSH_LOG_PACKET,"Processing %" PRIdS " bytes left in socket buffer", + receivedlen-processed); + rc = ssh_packet_socket_callback((char *)data + processed, + receivedlen - processed,user); + processed += rc; + } leave_function(); return processed; case PACKET_STATE_PROCESSING: - ssh_log(session, SSH_LOG_PACKET, "Nested packet processing. Delaying."); + ssh_log(session, SSH_LOG_RARE, "Nested packet processing. Delaying."); return 0; } |