u32 packet_decrypt_len(SSH_SESSION *session, char *crypted){ u32 decrypted; if(session->current_crypto) packet_decrypt(session,crypted,session->current_crypto->in_cipher->blocksize); memcpy(&decrypted,crypted,sizeof(decrypted)); ssh_log(session,SSH_LOG_PACKET,"packet size decrypted : %d (0x%lx)",ntohl(decrypted),ntohl(decrypted)); return ntohl(decrypted); }
u32 packet_decrypt_len(SSH_SESSION *session, char *crypted){ u32 decrypted; if(session->current_crypto) packet_decrypt(session,crypted,session->current_crypto->in_cipher->blocksize); memcpy(&decrypted,crypted,sizeof(decrypted)); ssh_say(3,"size decrypted : %lx\n",ntohl(decrypted)); return ntohl(decrypted); }
uint32_t packet_decrypt_len(ssh_session session, char *crypted){ uint32_t decrypted; if (session->current_crypto) { if (packet_decrypt(session, crypted, session->current_crypto->in_cipher->blocksize) < 0) { return 0; } } memcpy(&decrypted,crypted,sizeof(decrypted)); return ntohl(decrypted); }
u32 packet_decrypt_len(SSH_SESSION *session, char *crypted){ u32 decrypted; if (session->current_crypto) { if (packet_decrypt(session, crypted, session->current_crypto->in_cipher->blocksize) < 0) { return 0; } } memcpy(&decrypted,crypted,sizeof(decrypted)); ssh_log(session, SSH_LOG_PACKET, "Packet size decrypted: %lu (0x%lx)", (long unsigned int) ntohl(decrypted), (long unsigned int) ntohl(decrypted)); return ntohl(decrypted); }
i64 packet_verify_decrypt(u8 *output, u8 *key, u8 *signed_data, i64 signed_size) { u8 *aux_data; if (output && key && signed_data && signed_size >= (IPV4_AND_UDP_HEADER_SIZE + SIGN_AND_NONCE_BYTES)) { aux_data = malloc(signed_size - NONCE_BYTES); i64 rtnSize = -1; if (aux_data) { i64 auxSize = packet_verify(aux_data, key, signed_data, signed_size); if (auxSize > 0) { //do not decrypt unless verified rtnSize = packet_decrypt(output, key, aux_data, auxSize); } free(aux_data); } return rtnSize; } return -1; }
/** @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(const void *data, size_t receivedlen, void *user){ 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}; const void *packet = NULL; int to_be_read; int rc; uint32_t len, compsize, payloadsize; uint8_t padding; size_t processed=0; /* number of byte processed from the callback */ if (data == NULL) { goto error; } if (session->session_state == SSH_SESSION_STATE_ERROR) goto error; 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 */ 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 = ssh_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; /* FALL TROUGH */ 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 */ SSH_LOG(SSH_LOG_PACKET,"packet: partial packet (read len) [len=%d]",len); return processed; } packet = ((unsigned char *)data) + processed; // ssh_socket_read(session->socket,packet,to_be_read-current_macsize); 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_rest(session->in_buffer) + blocksize), buffer_get_rest_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; } processed += current_macsize; } /* 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; } if (padding > buffer_get_rest_len(session->in_buffer)) { ssh_set_error(session, SSH_FATAL, "Invalid padding: %d (%d left)", padding, buffer_get_rest_len(session->in_buffer)); goto error; } buffer_pass_bytes_end(session->in_buffer, padding); compsize = buffer_get_rest_len(session->in_buffer); #ifdef WITH_ZLIB if (session->current_crypto && session->current_crypto->do_compress_in && buffer_get_rest_len(session->in_buffer)) { if (decompress_buffer(session, session->in_buffer,MAX_PACKET_LEN) < 0) { goto error; } } #endif /* WITH_ZLIB */ payloadsize=buffer_get_rest_len(session->in_buffer); session->recv_seq++; /* We don't want to rewrite a new packet while still executing the packet callbacks */ session->packet_state = PACKET_STATE_PROCESSING; ssh_packet_parse_type(session); SSH_LOG(SSH_LOG_PACKET, "packet: read type %hhd [len=%d,padding=%hhd,comp=%d,payload=%d]", session->in_packet.type, len, padding, compsize, payloadsize); /* 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(SSH_LOG_PACKET,"Processing %" PRIdS " bytes left in socket buffer", receivedlen-processed); rc = ssh_packet_socket_callback(((unsigned char *)data) + processed, receivedlen - processed,user); processed += rc; } return processed; case PACKET_STATE_PROCESSING: SSH_LOG(SSH_LOG_RARE, "Nested packet processing. Delaying."); return 0; } ssh_set_error(session, SSH_FATAL, "Invalid state into packet_read2(): %d", session->packet_state); error: session->session_state= SSH_SESSION_STATE_ERROR; return processed; }
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) < 0) { 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)); #endif 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 ..."); 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); error: leave_function(); return rc; }
/* a slighty modified packet_read2() for SSH-1 protocol */ static int packet_read1(SSH_SESSION *session) { void *packet = NULL; int rc = SSH_ERROR; int to_be_read; u32 padding; u32 crc; u32 len; enter_function(); if(!session->alive) { goto 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_read(session->socket, &len, sizeof(u32)); if (rc != SSH_OK) { goto error; } rc = SSH_ERROR; /* len is not encrypted */ len = ntohl(len); if (len > MAX_PACKET_LEN) { ssh_set_error(session, SSH_FATAL, "read_packet(): Packet len too high (%u %.8x)", len, len); goto error; } ssh_log(session, SSH_LOG_PACKET, "Reading a %d bytes packet", len); session->in_packet.len = len; session->packet_state = PACKET_STATE_SIZEREAD; case PACKET_STATE_SIZEREAD: len = session->in_packet.len; /* SSH-1 has a fixed padding lenght */ padding = 8 - (len % 8); to_be_read = len + padding; /* it is _not_ possible that to_be_read be < 8. */ packet = malloc(to_be_read); if (packet == NULL) { ssh_set_error(session, SSH_FATAL, "Not enough space"); goto error; } rc = ssh_socket_read(session->socket, packet, to_be_read); if(rc != SSH_OK) { SAFE_FREE(packet); goto error; } rc = SSH_ERROR; if (buffer_add_data(session->in_buffer,packet,to_be_read) < 0) { SAFE_FREE(packet); goto error; } SAFE_FREE(packet); #ifdef DEBUG_CRYPTO ssh_print_hexa("read packet:", buffer_get(session->in_buffer), buffer_get_len(session->in_buffer)); #endif if (session->current_crypto) { /* * We decrypt everything, missing the lenght part (which was * previously read, unencrypted, and is not part of the buffer */ if (packet_decrypt(session, buffer_get(session->in_buffer), buffer_get_len(session->in_buffer)) < 0) { ssh_set_error(session, SSH_FATAL, "Packet decrypt error"); goto error; } } #ifdef DEBUG_CRYPTO ssh_print_hexa("read packet decrypted:", buffer_get(session->in_buffer), buffer_get_len(session->in_buffer)); #endif ssh_log(session, SSH_LOG_PACKET, "%d bytes padding", padding); if(((len + padding) != buffer_get_rest_len(session->in_buffer)) || ((len + padding) < sizeof(u32))) { ssh_log(session, SSH_LOG_RARE, "no crc32 in packet"); ssh_set_error(session, SSH_FATAL, "no crc32 in packet"); goto error; } memcpy(&crc, buffer_get_rest(session->in_buffer) + (len+padding) - sizeof(u32), sizeof(u32)); buffer_pass_bytes_end(session->in_buffer, sizeof(u32)); crc = ntohl(crc); if (ssh_crc32(buffer_get_rest(session->in_buffer), (len + padding) - sizeof(u32)) != crc) { #ifdef DEBUG_CRYPTO ssh_print_hexa("crc32 on",buffer_get_rest(session->in_buffer), len + padding - sizeof(u32)); #endif ssh_log(session, SSH_LOG_RARE, "Invalid crc32"); ssh_set_error(session, SSH_FATAL, "Invalid crc32: expected %.8x, got %.8x", crc, ssh_crc32(buffer_get_rest(session->in_buffer), len + padding - sizeof(u32))); goto error; } /* pass the padding */ buffer_pass_bytes(session->in_buffer, padding); ssh_log(session, SSH_LOG_PACKET, "The packet is valid"); /* TODO FIXME #if defined(HAVE_LIBZ) && defined(WITH_LIBZ) if(session->current_crypto && session->current_crypto->do_compress_in){ decompress_buffer(session,session->in_buffer); } #endif */ session->recv_seq++; session->packet_state=PACKET_STATE_INIT; leave_function(); return SSH_OK; } /* switch */ ssh_set_error(session, SSH_FATAL, "Invalid state into packet_read1(): %d", session->packet_state); error: leave_function(); return rc; }
int ssh_packet_socket_callback1(const void *data, size_t receivedlen, void *user) { void *packet = NULL; int to_be_read; size_t processed=0; uint32_t padding; uint32_t crc; uint32_t len; ssh_session session=(ssh_session)user; 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 = ssh_buffer_new(); if (session->in_buffer == NULL) { goto error; } } /* must have at least enough bytes for size */ if(receivedlen < sizeof(uint32_t)){ return 0; } memcpy(&len,data,sizeof(uint32_t)); processed += sizeof(uint32_t); /* len is not encrypted */ len = ntohl(len); if (len > MAX_PACKET_LEN) { ssh_set_error(session, SSH_FATAL, "read_packet(): Packet len too high (%u %.8x)", len, len); goto error; } ssh_log(session, SSH_LOG_PACKET, "Reading a %d bytes packet", len); session->in_packet.len = len; session->packet_state = PACKET_STATE_SIZEREAD; case PACKET_STATE_SIZEREAD: len = session->in_packet.len; /* SSH-1 has a fixed padding lenght */ padding = 8 - (len % 8); to_be_read = len + padding; if(to_be_read + processed > receivedlen){ /* wait for rest of packet */ return processed; } /* it is _not_ possible that to_be_read be < 8. */ packet = (char *)data + processed; if (buffer_add_data(session->in_buffer,packet,to_be_read) < 0) { SAFE_FREE(packet); goto error; } processed += to_be_read; #ifdef DEBUG_CRYPTO ssh_print_hexa("read packet:", ssh_buffer_get_begin(session->in_buffer), ssh_buffer_get_len(session->in_buffer)); #endif if (session->current_crypto) { /* * We decrypt everything, missing the lenght part (which was * previously read, unencrypted, and is not part of the buffer */ if (packet_decrypt(session, ssh_buffer_get_begin(session->in_buffer), ssh_buffer_get_len(session->in_buffer)) < 0) { ssh_set_error(session, SSH_FATAL, "Packet decrypt error"); goto error; } } #ifdef DEBUG_CRYPTO ssh_print_hexa("read packet decrypted:", ssh_buffer_get_begin(session->in_buffer), ssh_buffer_get_len(session->in_buffer)); #endif ssh_log(session, SSH_LOG_PACKET, "%d bytes padding", padding); if(((len + padding) != buffer_get_rest_len(session->in_buffer)) || ((len + padding) < sizeof(uint32_t))) { ssh_log(session, SSH_LOG_RARE, "no crc32 in packet"); ssh_set_error(session, SSH_FATAL, "no crc32 in packet"); goto error; } memcpy(&crc, (unsigned char *)buffer_get_rest(session->in_buffer) + (len+padding) - sizeof(uint32_t), sizeof(uint32_t)); buffer_pass_bytes_end(session->in_buffer, sizeof(uint32_t)); crc = ntohl(crc); if (ssh_crc32(buffer_get_rest(session->in_buffer), (len + padding) - sizeof(uint32_t)) != crc) { #ifdef DEBUG_CRYPTO ssh_print_hexa("crc32 on",buffer_get_rest(session->in_buffer), len + padding - sizeof(uint32_t)); #endif ssh_log(session, SSH_LOG_RARE, "Invalid crc32"); ssh_set_error(session, SSH_FATAL, "Invalid crc32: expected %.8x, got %.8x", crc, ssh_crc32(buffer_get_rest(session->in_buffer), len + padding - sizeof(uint32_t))); goto error; } /* pass the padding */ buffer_pass_bytes(session->in_buffer, padding); ssh_log(session, SSH_LOG_PACKET, "The packet is valid"); /* TODO FIXME #if defined(HAVE_LIBZ) && defined(WITH_LIBZ) if(session->current_crypto && session->current_crypto->do_compress_in){ decompress_buffer(session,session->in_buffer); } #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; ssh_packet_parse_type(session); /* execute callbacks */ ssh_packet_process(session, session->in_packet.type); session->packet_state = PACKET_STATE_INIT; if(processed < receivedlen){ int rc; /* 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_callback1((char *)data + processed, receivedlen - processed,user); processed += rc; } return processed; case PACKET_STATE_PROCESSING: ssh_log(session, SSH_LOG_RARE, "Nested packet processing. Delaying."); return 0; } error: session->session_state=SSH_SESSION_STATE_ERROR; return processed; }