diff options
author | Andreas Schneider <mail@cynapses.org> | 2009-04-10 09:26:18 +0000 |
---|---|---|
committer | Andreas Schneider <mail@cynapses.org> | 2009-04-10 09:26:18 +0000 |
commit | e0ae9635ea829b15782de4dd43e31ae67ef7fffe (patch) | |
tree | 8382084d17be2ac260083c47811de8345e11eba3 /libssh/packet.c | |
parent | e4624d6ed7f6506c0d93ca22fa04b10fc4f16894 (diff) | |
download | libssh-e0ae9635ea829b15782de4dd43e31ae67ef7fffe.tar.gz libssh-e0ae9635ea829b15782de4dd43e31ae67ef7fffe.tar.xz libssh-e0ae9635ea829b15782de4dd43e31ae67ef7fffe.zip |
Add more error checks to packet_read2().
git-svn-id: svn+ssh://svn.berlios.de/svnroot/repos/libssh/trunk@454 7dcaeef0-15fb-0310-b436-a5af3365683c
Diffstat (limited to 'libssh/packet.c')
-rw-r--r-- | libssh/packet.c | 279 |
1 files changed, 160 insertions, 119 deletions
diff --git a/libssh/packet.c b/libssh/packet.c index 179341f..1b19b82 100644 --- a/libssh/packet.c +++ b/libssh/packet.c @@ -46,129 +46,170 @@ static int macsize=SHA_DIGEST_LEN; #define PACKET_STATE_INIT 0 #define PACKET_STATE_SIZEREAD 1 -static int packet_read2(SSH_SESSION *session){ - u32 len; - void *packet=NULL; - unsigned char mac[30]; - char buffer[16]; - int to_be_read; - int ret; - u8 padding; - unsigned int blocksize=(session->current_crypto?session->current_crypto->in_cipher->blocksize:8); - int current_macsize=session->current_crypto?macsize:0; - enter_function(); - if(!session->alive){ - leave_function(); - return SSH_ERROR; // the error message was already set into this session - } - switch(session->packet_state){ - case PACKET_STATE_INIT: - memset(&session->in_packet,0,sizeof(PACKET)); - if(session->in_buffer) - buffer_reinit(session->in_buffer); - else - session->in_buffer=buffer_new(); - ret=ssh_socket_wait_for_data(session->socket,session,blocksize); - if(ret != SSH_OK){ - leave_function(); - return ret; // can be SSH_ERROR or SSH_AGAIN - } -// be_read=completeread(session->fd,buffer,blocksize); - // can't fail since we're sure there is enough data in socket buffer - ssh_socket_read(session->socket,buffer,blocksize); - len=packet_decrypt_len(session,buffer); - buffer_add_data(session->in_buffer,buffer,blocksize); - if(len> MAX_PACKET_LEN){ - ssh_set_error(session, SSH_FATAL, - "read_packet(): Packet len too high(%u %.4x)", len, len); - leave_function(); - return SSH_ERROR; - } - to_be_read=len-blocksize+sizeof(u32); - 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); - leave_function(); - return SSH_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(u32) + current_macsize; - /* if to_be_read is zero, the whole packet was blocksize bytes. */ - if(to_be_read != 0){ - ret=ssh_socket_wait_for_data(session->socket,session,to_be_read); - if(ret!=SSH_OK){ - leave_function(); - return ret; - } - packet = malloc(to_be_read); - if (packet == NULL) { - ssh_set_error(session, SSH_FATAL, "No space left"); - leave_function(); - return SSH_ERROR; - } - ssh_socket_read(session->socket,packet,to_be_read-current_macsize); - ssh_log(session,SSH_LOG_PACKET,"Read a %d bytes packet",len); - buffer_add_data(session->in_buffer,packet,to_be_read-current_macsize); - free(packet); - } - if(session->current_crypto){ - /* decrypt the rest of the packet (blocksize bytes already have been decrypted */ - if (packet_decrypt(session, - buffer_get(session->in_buffer) + blocksize, - buffer_get_len(session->in_buffer) - blocksize) < 0) { - ssh_set_error(session, SSH_FATAL, "Decrypt error"); - leave_function(); - return SSH_ERROR; - } - ssh_socket_read(session->socket,mac,macsize); - if(packet_hmac_verify(session,session->in_buffer,mac)){ - ssh_set_error(session,SSH_FATAL,"HMAC error"); - leave_function(); - return SSH_ERROR; - } - } - buffer_pass_bytes(session->in_buffer,sizeof(u32)); - /*pass the size which has been processed before*/ - if(!buffer_get_u8(session->in_buffer,&padding)){ - ssh_set_error(session,SSH_FATAL,"Packet too short to read padding"); - leave_function(); - return SSH_ERROR; - } - ssh_log(session, SSH_LOG_RARE, - "%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)); +static int packet_read2(SSH_SESSION *session) { + 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; + + u32 len; + u8 padding; + + enter_function(); + + if (session->alive == 0) { + /* The error message was already set into this session */ + leave_function(); + return SSH_ERROR; + } + + switch(session->packet_state) { + case PACKET_STATE_INIT: + 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; + } + } + + rc = ssh_socket_wait_for_data(session->socket, session, blocksize); + if (rc != SSH_OK) { + goto error; + } + rc = SSH_ERROR; + /* can't fail since we're sure there is enough data in socket buffer */ + ssh_socket_read(session->socket, buffer, 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(u32); + 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(u32) + current_macsize; + /* if to_be_read is zero, the whole packet was blocksize bytes. */ + if (to_be_read != 0) { + rc = ssh_socket_wait_for_data(session->socket,session,to_be_read); + if (rc != SSH_OK) { + goto error; + } + rc = SSH_ERROR; + + packet = malloc(to_be_read); + if (packet == NULL) { + ssh_set_error(session, SSH_FATAL, "No space left"); + goto error; + } + 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) { + SAFE_FREE(packet); + goto error; + } + SAFE_FREE(packet); + } + + if (session->current_crypto) { + /* + * decrypt the rest of the packet (blocksize bytes already + * have been decrypted) + */ + if (packet_decrypt(session, + buffer_get(session->in_buffer) + blocksize, + buffer_get_len(session->in_buffer) - blocksize) < 0) { + ssh_set_error(session, SSH_FATAL, "Decrypt error"); + goto error; + } + ssh_socket_read(session->socket, mac, macsize); + + if (packet_hmac_verify(session, session->in_buffer, mac)) { + ssh_set_error(session, SSH_FATAL, "HMAC error"); + goto error; + } + } + + buffer_pass_bytes(session->in_buffer, sizeof(u32)); + + /* pass the size which has been processed before */ + 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_RARE, + "%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)); + ssh_print_hexa("incrimined packet", + buffer_get(session->in_buffer), + buffer_get_len(session->in_buffer)); #endif - leave_function(); - return SSH_ERROR; - } - buffer_pass_bytes_end(session->in_buffer,padding); - ssh_log(session, SSH_LOG_RARE, - "After padding, %d bytes left in buffer", - buffer_get_rest_len(session->in_buffer)); + goto error; + } + buffer_pass_bytes_end(session->in_buffer, padding); + + ssh_log(session, SSH_LOG_RARE, + "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_RARE, "Decompressing in_buffer ..."); - decompress_buffer(session,session->in_buffer); - } + if (session->current_crypto && session->current_crypto->do_compress_in) { + ssh_log(session, SSH_LOG_RARE, "Decompressing in_buffer ..."); + if (decompress_buffer(session, session->in_buffer) < 0) { + goto error; + } + } #endif - session->recv_seq++; - session->packet_state=PACKET_STATE_INIT; - leave_function(); - return SSH_OK; - } - ssh_set_error(session,SSH_FATAL,"Invalid state into packet_read2() : %d",session->packet_state); - leave_function(); - return SSH_ERROR; + session->recv_seq++; + session->packet_state = PACKET_STATE_INIT; + + leave_function(); + return SSH_OK; + } + + ssh_set_error(session, SSH_FATAL, + "Invalid state into packet_read2(): %d", + session->packet_state); + +error: + leave_function(); + return rc; } #ifdef HAVE_SSH1 |