aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAris Adamantiadis <aris@0xbadc0de.be>2009-12-13 18:20:05 +0100
committerAris Adamantiadis <aris@0xbadc0de.be>2009-12-13 18:20:05 +0100
commitc92f54102eebe024c8975a96947b35a5300be5d4 (patch)
tree9d2d60f1e0fd38f2f237d569466906a4cde5dd88
parent964d5f88cceca5134ad03822ad1b1c47669f7d5c (diff)
downloadlibssh-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.h2
-rw-r--r--libssh/client.c280
-rw-r--r--libssh/packet.c14
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;
}