summaryrefslogtreecommitdiffstats
path: root/libssh/packet.c
diff options
context:
space:
mode:
authorAris Adamantiadis <aris@0xbadc0de.be>2009-11-27 20:42:43 +0100
committerAris Adamantiadis <aris@0xbadc0de.be>2009-11-27 20:42:43 +0100
commit91bb1b2de66a62723e0cbdda02e455f728522181 (patch)
tree2609d87fcf24107e3507237ec22a80da37994f7c /libssh/packet.c
parent810adadf2e8a5c68460b864994055ece6f1a3096 (diff)
downloadlibssh-91bb1b2de66a62723e0cbdda02e455f728522181.tar.gz
libssh-91bb1b2de66a62723e0cbdda02e455f728522181.tar.xz
libssh-91bb1b2de66a62723e0cbdda02e455f728522181.zip
Squashed commit of the following:
commit 43fad8dfd977637c31fade76ace2905f6528c3bc Author: Aris Adamantiadis <aris@0xbadc0de.be> Date: Fri Nov 27 18:39:06 2009 +0100 adaptation to the new ssh_poll_handle object name commit 1e5e6ac4605adf10d437d04f0fd4b7e66024853c Merge: 3fd92a0... 810adad... Author: Aris Adamantiadis <aris@0xbadc0de.be> Date: Fri Nov 27 18:33:06 2009 +0100 Merge branch 'master' into badcode/libssh_async commit 3fd92a08eb74b1447a9ff4ca4e1d137475c62cc6 Author: Aris Adamantiadis <aris@0xbadc0de.be> Date: Mon Nov 2 14:25:46 2009 +0100 Compiles again commit 8910d7b9692418c9ccea0234f6d49674d238dc16 Merge: e83f1b5... cce34a6... Author: Aris Adamantiadis <aris@0xbadc0de.be> Date: Mon Nov 2 12:47:34 2009 +0100 Merge branch 'master' into libssh_async Very big merge ! Conflicts: include/libssh/callbacks.h include/libssh/priv.h libssh/channels.c libssh/messages.c libssh/packet.c libssh/server.c libssh/session.c libssh/socket.c commit e83f1b593219e183082b015315f09bfe95a29cfc Author: Aris Adamantiadis <aris@0xbadc0de.be> Date: Mon Nov 2 12:07:01 2009 +0100 rename callback.h commit dffa7b730e8f39e2198de18ab69a8e57bef95e58 Merge: 5a8b748... de8808c... Author: Aris Adamantiadis <aris@0xbadc0de.be> Date: Tue Sep 15 10:50:07 2009 +0200 Merge branch 'master' of git://git.libssh.org/projects/libssh/libssh into libssh_async commit 5a8b7484f36599d28f2c0c14a23b76bfc7257638 Author: Aris Adamantiadis <aris@0xbadc0de.be> Date: Sun Sep 13 12:55:18 2009 +0200 More updates to callback system commit 18620c20d5e4e62107093f7fd330e553493253fa Author: Aris Adamantiadis <aris@0xbadc0de.be> Date: Sat Sep 12 22:26:52 2009 +0200 Same thing with channel_rcv_data commit fc4a56f6726e409a5866272923f1cbebfc821af3 Author: Aris Adamantiadis <aris@0xbadc0de.be> Date: Sat Sep 12 22:17:45 2009 +0200 added a few packet handlers for channels commit 4b6bb4fd00b10cf1321a764126f277ab204bffe3 Author: Aris Adamantiadis <aris@0xbadc0de.be> Date: Fri Sep 11 23:15:25 2009 +0300 sample packet handlers + bugfixes commit 2784d09d6dec0a8f868912d14f90d860233b3f82 Author: Aris Adamantiadis <aris@0xbadc0de.be> Date: Fri Sep 11 20:30:50 2009 +0300 Packet callbacks nearly finished Need tests and implementation of some packet callbacks commit cd3ea43f20c9ae2f54576ca98a0ea75c5d4299d3 Author: Aris Adamantiadis <aris@0xbadc0de.be> Date: Thu Sep 10 12:46:02 2009 +0300 First step of async packet handling The socket to packet handler is nearly done (needs testing) I still need to define the interface for callbacks. commit 487f4d2a900a5fe3b90ceda4460ab7d38d7ad722 Author: Aris Adamantiadis <aris@0xbadc0de.be> Date: Tue Sep 8 23:24:09 2009 +0300 Almost complete socket callback system Finished the callback function so it bufferizes data when callee does not use it. Flushes the buffer automaticaly after a ssh_socket_nonblocking_flush commit 23571f22fac9e40c855dfa99569bba181a39648b Author: Aris Adamantiadis <aris@0xbadc0de.be> Date: Tue Sep 8 22:22:32 2009 +0300 First draft of a callback system
Diffstat (limited to 'libssh/packet.c')
-rw-r--r--libssh/packet.c316
1 files changed, 288 insertions, 28 deletions
diff --git a/libssh/packet.c b/libssh/packet.c
index 84c6466..0c02323 100644
--- a/libssh/packet.c
+++ b/libssh/packet.c
@@ -40,10 +40,57 @@
#include "libssh/packet.h"
#include "libssh/socket.h"
#include "libssh/channels.h"
+#include "libssh/misc.h"
#include "libssh/session.h"
#include "libssh/messages.h"
#include "libssh/pcap.h"
+ssh_packet_callback default_packet_handlers[]= {
+
+ ssh_packet_disconnect_callback, //#define SSH2_MSG_DISCONNECT 1
+ ssh_packet_ignore_callback, //#define SSH2_MSG_IGNORE 2
+ NULL, //#define SSH2_MSG_UNIMPLEMENTED 3
+ ssh_packet_ignore_callback, //#define SSH2_MSG_DEBUG 4
+ NULL, //#define SSH2_MSG_SERVICE_REQUEST 5
+ NULL, //#define SSH2_MSG_SERVICE_ACCEPT 6
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, // 7-19
+ NULL, //#define SSH2_MSG_KEXINIT 20
+ NULL, //#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
+ 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
+ NULL, NULL, NULL, NULL, NULL, // 35-49
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, //#define SSH2_MSG_USERAUTH_REQUEST 50
+ NULL, //#define SSH2_MSG_USERAUTH_FAILURE 51
+ NULL, //#define SSH2_MSG_USERAUTH_SUCCESS 52
+ NULL, //#define SSH2_MSG_USERAUTH_BANNER 53
+ NULL, //#define SSH2_MSG_USERAUTH_PK_OK 60 SSH2_MSG_USERAUTH_PASSWD_CHANGEREQ 60
+ //SSH2_MSG_USERAUTH_INFO_REQUEST 60
+ NULL, //#define SSH2_MSG_USERAUTH_INFO_RESPONSE 61
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, //62-79
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, //#define SSH2_MSG_GLOBAL_REQUEST 80
+ NULL, //#define SSH2_MSG_REQUEST_SUCCESS 81
+ NULL, //#define SSH2_MSG_REQUEST_FAILURE 82
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, // 83-89
+ NULL, //#define SSH2_MSG_CHANNEL_OPEN 90
+ NULL, //#define SSH2_MSG_CHANNEL_OPEN_CONFIRMATION 91
+ NULL, //#define SSH2_MSG_CHANNEL_OPEN_FAILURE 92
+ channel_rcv_change_window, //#define SSH2_MSG_CHANNEL_WINDOW_ADJUST 93
+ channel_rcv_data, //#define SSH2_MSG_CHANNEL_DATA 94
+ channel_rcv_data, //#define SSH2_MSG_CHANNEL_EXTENDED_DATA 95
+ channel_rcv_eof, //#define SSH2_MSG_CHANNEL_EOF 96
+ channel_rcv_close, //#define SSH2_MSG_CHANNEL_CLOSE 97
+ channel_rcv_request, //#define SSH2_MSG_CHANNEL_REQUEST 98
+ NULL, //#define SSH2_MSG_CHANNEL_SUCCESS 99
+ NULL, //#define SSH2_MSG_CHANNEL_FAILURE 100
+};
+
/* XXX include selected mac size */
static int macsize=SHA_DIGEST_LEN;
@@ -54,6 +101,243 @@ static int macsize=SHA_DIGEST_LEN;
#define PACKET_STATE_INIT 0
#define PACKET_STATE_SIZEREAD 1
+#define PACKET_STATE_PROCESSING 2
+
+/** @internal
+ * @handles a data received event. It then calls the handlers for the different packet types
+ * or and exception handler callback.
+ * @param user pointer to current ssh_session
+ * @param data pointer to the data received
+ * @len length of data received. It might not be enough for a complete packet
+ * @returns number of bytes read and processed.
+ */
+int ssh_packet_socket_callback(void *user, const void *data, size_t receivedlen){
+ ssh_session session=(ssh_session) user;
+ unsigned int blocksize = (session->current_crypto ?
+ session->current_crypto->in_cipher->blocksize : 8);
+ int current_macsize = session->current_crypto ? macsize : 0;
+ unsigned char mac[30] = {0};
+ char buffer[16] = {0};
+ void *packet=NULL;
+ int to_be_read;
+ int rc = SSH_ERROR;
+ uint32_t len;
+ uint8_t padding;
+ size_t processed=0; /* number of byte processed from the callback */
+
+ enter_function();
+
+ switch(session->packet_state) {
+ case PACKET_STATE_INIT:
+ if(receivedlen < blocksize){
+ /* We didn't receive enough data to read at least one block size, give up */
+ leave_function();
+ return 0;
+ }
+ memset(&session->in_packet, 0, sizeof(PACKET));
+
+ if (session->in_buffer) {
+ if (buffer_reinit(session->in_buffer) < 0) {
+ goto error;
+ }
+ } else {
+ session->in_buffer = buffer_new();
+ if (session->in_buffer == NULL) {
+ goto error;
+ }
+ }
+
+ memcpy(buffer,data,blocksize);
+ processed += blocksize;
+ len = packet_decrypt_len(session, buffer);
+
+ if (buffer_add_data(session->in_buffer, buffer, blocksize) < 0) {
+ goto error;
+ }
+
+ if(len > MAX_PACKET_LEN) {
+ ssh_set_error(session, SSH_FATAL,
+ "read_packet(): Packet len too high(%u %.4x)", len, len);
+ goto error;
+ }
+
+ to_be_read = len - blocksize + sizeof(uint32_t);
+ if (to_be_read < 0) {
+ /* remote sshd sends invalid sizes? */
+ ssh_set_error(session, SSH_FATAL,
+ "given numbers of bytes left to be read < 0 (%d)!", to_be_read);
+ goto error;
+ }
+
+ /* saves the status of the current operations */
+ session->in_packet.len = len;
+ session->packet_state = PACKET_STATE_SIZEREAD;
+ case PACKET_STATE_SIZEREAD:
+ len = session->in_packet.len;
+ to_be_read = len - blocksize + sizeof(uint32_t) + current_macsize;
+ /* if to_be_read is zero, the whole packet was blocksize bytes. */
+ if (to_be_read != 0) {
+ if(receivedlen - processed < (unsigned int)to_be_read){
+ /* give up, not enough data in buffer */
+ return processed;
+ }
+ rc = SSH_ERROR;
+
+ packet = (unsigned char *)data + processed;
+// ssh_socket_read(session->socket,packet,to_be_read-current_macsize);
+
+ ssh_log(session,SSH_LOG_PACKET,"Read a %d bytes packet",len);
+
+ if (buffer_add_data(session->in_buffer, packet,
+ to_be_read - current_macsize) < 0) {
+ goto error;
+ }
+ processed += to_be_read - current_macsize;
+ }
+
+ if (session->current_crypto) {
+ /*
+ * decrypt the rest of the packet (blocksize bytes already
+ * have been decrypted)
+ */
+ if (packet_decrypt(session,
+ ((uint8_t*)buffer_get(session->in_buffer) + blocksize),
+ buffer_get_len(session->in_buffer) - blocksize) < 0) {
+ ssh_set_error(session, SSH_FATAL, "Decrypt error");
+ goto error;
+ }
+ /* copy the last part from the incoming buffer */
+ memcpy(mac,(unsigned char *)packet + to_be_read - current_macsize, macsize);
+
+ if (packet_hmac_verify(session, session->in_buffer, mac) < 0) {
+ ssh_set_error(session, SSH_FATAL, "HMAC error");
+ goto error;
+ }
+ }
+
+ /* skip the size field which has been processed before */
+ buffer_pass_bytes(session->in_buffer, sizeof(uint32_t));
+
+ if (buffer_get_u8(session->in_buffer, &padding) == 0) {
+ ssh_set_error(session, SSH_FATAL, "Packet too short to read padding");
+ goto error;
+ }
+
+ ssh_log(session, SSH_LOG_PACKET,
+ "%hhd bytes padding, %d bytes left in buffer",
+ padding, buffer_get_rest_len(session->in_buffer));
+
+ if (padding > buffer_get_rest_len(session->in_buffer)) {
+ ssh_set_error(session, SSH_FATAL,
+ "Invalid padding: %d (%d resting)",
+ padding,
+ buffer_get_rest_len(session->in_buffer));
+#ifdef DEBUG_CRYPTO
+ ssh_print_hexa("incrimined packet",
+ buffer_get(session->in_buffer),
+ buffer_get_len(session->in_buffer));
+#endif
+ goto error;
+ }
+ buffer_pass_bytes_end(session->in_buffer, padding);
+
+ ssh_log(session, SSH_LOG_PACKET,
+ "After padding, %d bytes left in buffer",
+ buffer_get_rest_len(session->in_buffer));
+#if defined(HAVE_LIBZ) && defined(WITH_LIBZ)
+ if (session->current_crypto && session->current_crypto->do_compress_in) {
+ ssh_log(session, SSH_LOG_PACKET, "Decompressing in_buffer ...");
+ if (decompress_buffer(session, session->in_buffer,MAX_PACKET_LEN) < 0) {
+ goto error;
+ }
+ }
+#endif
+ session->recv_seq++;
+ /* We don't want to rewrite a new packet while still executing the packet callbacks */
+ session->packet_state = PACKET_STATE_PROCESSING;
+ packet_translate(session);
+ /* execute callbacks */
+ ssh_packet_process(session, session->in_packet.type);
+ session->packet_state = PACKET_STATE_INIT;
+ leave_function();
+ return processed;
+ case PACKET_STATE_PROCESSING:
+ ssh_log(session, SSH_LOG_PACKET, "Nested packet processing. Delaying.");
+ return 0;
+ }
+
+ ssh_set_error(session, SSH_FATAL,
+ "Invalid state into packet_read2(): %d",
+ session->packet_state);
+
+error:
+ leave_function();
+ return processed;
+}
+
+void ssh_packet_register_socket_callback(ssh_session session, struct socket *s){
+ session->socket_callbacks.data=ssh_packet_socket_callback;
+ session->socket_callbacks.connected=NULL;
+ session->socket_callbacks.controlflow=NULL;
+ session->socket_callbacks.exception=NULL;
+ session->socket_callbacks.user=session;
+ ssh_socket_set_callbacks(s,&session->socket_callbacks);
+}
+
+/** @internal
+ * @brief sets the callbacks for the packet layer
+ */
+void ssh_packet_set_callbacks(ssh_session session, ssh_packet_callbacks callbacks){
+ if(session->packet_callbacks == NULL){
+ session->packet_callbacks = ssh_list_new();
+ }
+ ssh_list_add(session->packet_callbacks,callbacks);
+}
+
+/** @internal
+ * @brief sets the default packet handlers
+ */
+void ssh_packet_set_default_callbacks(ssh_session session){
+ session->default_packet_callbacks.start=0;
+ session->default_packet_callbacks.n_callbacks=sizeof(default_packet_handlers)/sizeof(ssh_packet_callback);
+ session->default_packet_callbacks.user=session;
+ session->default_packet_callbacks.callbacks=default_packet_handlers;
+ ssh_packet_set_callbacks(session, &session->default_packet_callbacks);
+}
+
+/** @internal
+ * @brief dispatch the call of packet handlers callbacks for a received packet
+ * @param type type of packet
+ */
+void ssh_packet_process(ssh_session session, u_int8_t type){
+ struct ssh_iterator *i;
+ int r;
+ ssh_packet_callbacks cb;
+ enter_function();
+ ssh_log(session,SSH_LOG_PACKET, "Dispatching handler for packet type %d",type);
+ if(session->packet_callbacks == NULL){
+ ssh_log(session,SSH_LOG_RARE,"Packet callback is not initialized !");
+ goto error;
+ }
+ i=ssh_list_get_iterator(session->packet_callbacks);
+ while(i != NULL){
+ cb=ssh_iterator_value(ssh_packet_callbacks,i);
+ i=i->next;
+ if(!cb)
+ continue;
+ if(cb->start > type)
+ continue;
+ if(cb->start + cb->n_callbacks > type)
+ continue;
+ if(cb->callbacks[type - cb->start]==NULL)
+ continue;
+ r=cb->callbacks[type - cb->start](session,cb->user,type,session->in_buffer);
+ if(r==SSH_PACKET_USED)
+ break;
+ }
+error:
+ leave_function();
+}
static int packet_read2(ssh_session session) {
unsigned int blocksize = (session->current_crypto ?
@@ -624,10 +908,7 @@ int packet_send(ssh_session session) {
}
void packet_parse(ssh_session session) {
- ssh_string error_s = NULL;
- char *error = NULL;
- uint32_t type = session->in_packet.type;
- uint32_t tmp;
+ uint8_t type = session->in_packet.type;
#ifdef WITH_SSH1
if (session->version == 1) {
@@ -657,41 +938,20 @@ void packet_parse(ssh_session session) {
#endif /* WITH_SSH1 */
switch(type) {
case SSH2_MSG_DISCONNECT:
- buffer_get_u32(session->in_buffer, &tmp);
- error_s = buffer_get_ssh_string(session->in_buffer);
- if (error_s == NULL) {
- return;
- }
- error = string_to_char(error_s);
- string_free(error_s);
- if (error == NULL) {
- return;
- }
- ssh_log(session, SSH_LOG_PACKET, "Received SSH_MSG_DISCONNECT\n");
- ssh_set_error(session, SSH_FATAL,
- "Received SSH_MSG_DISCONNECT: %s",error);
-
- SAFE_FREE(error);
-
- ssh_socket_close(session->socket);
- session->alive = 0;
-
- return;
case SSH2_MSG_CHANNEL_WINDOW_ADJUST:
case SSH2_MSG_CHANNEL_DATA:
case SSH2_MSG_CHANNEL_EXTENDED_DATA:
case SSH2_MSG_CHANNEL_REQUEST:
case SSH2_MSG_CHANNEL_EOF:
case SSH2_MSG_CHANNEL_CLOSE:
- channel_handle(session,type);
- return;
case SSH2_MSG_IGNORE:
case SSH2_MSG_DEBUG:
+ ssh_packet_process(session,type);
return;
case SSH2_MSG_SERVICE_REQUEST:
case SSH2_MSG_USERAUTH_REQUEST:
case SSH2_MSG_CHANNEL_OPEN:
- message_handle(session,type);
+ message_handle(session,NULL,type,session->in_buffer);
return;
default:
ssh_log(session, SSH_LOG_RARE, "Received unhandled packet %d", type);
@@ -735,7 +995,7 @@ static int packet_wait1(ssh_session session, int type, int blocking) {
/* case SSH2_MSG_CHANNEL_CLOSE:
packet_parse(session);
break;;
- */
+ */
default:
if (type && (type != session->in_packet.type)) {
ssh_set_error(session, SSH_FATAL,