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, buffer_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 (ssh_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(SSH_LOG_PACKET, "Reading a %d bytes packet", len); session->in_packet.len = len; session->packet_state = PACKET_STATE_SIZEREAD; /* FALL THROUGH */ 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 (ssh_buffer_add_data(session->in_buffer,packet,to_be_read) < 0) { goto error; } processed += to_be_read; #ifdef DEBUG_CRYPTO ssh_print_hexa("read packet:", ssh_buffer_get(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 */ buffer_len = ssh_buffer_get_len(session->in_buffer); if (buffer_len > 0) { int rc; rc = ssh_packet_decrypt(session, ssh_buffer_get(session->in_buffer), buffer_len); if (rc < 0) { ssh_set_error(session, SSH_FATAL, "Packet decrypt error"); goto error; } } } #ifdef DEBUG_CRYPTO ssh_print_hexa("read packet decrypted:", ssh_buffer_get(session->in_buffer), ssh_buffer_get_len(session->in_buffer)); #endif SSH_LOG(SSH_LOG_PACKET, "%d bytes padding", padding); if(((len + padding) != ssh_buffer_get_len(session->in_buffer)) || ((len + padding) < sizeof(uint32_t))) { SSH_LOG(SSH_LOG_RARE, "no crc32 in packet"); ssh_set_error(session, SSH_FATAL, "no crc32 in packet"); goto error; } memcpy(&crc, (unsigned char *)ssh_buffer_get(session->in_buffer) + (len+padding) - sizeof(uint32_t), sizeof(uint32_t)); ssh_buffer_pass_bytes_end(session->in_buffer, sizeof(uint32_t)); crc = ntohl(crc); if (ssh_crc32(ssh_buffer_get(session->in_buffer), (len + padding) - sizeof(uint32_t)) != crc) { #ifdef DEBUG_CRYPTO ssh_print_hexa("crc32 on",ssh_buffer_get(session->in_buffer), len + padding - sizeof(uint32_t)); #endif SSH_LOG(SSH_LOG_RARE, "Invalid crc32"); ssh_set_error(session, SSH_FATAL, "Invalid crc32: expected %.8x, got %.8x", crc, ssh_crc32(ssh_buffer_get(session->in_buffer), len + padding - sizeof(uint32_t))); goto error; } /* pass the padding */ ssh_buffer_pass_bytes(session->in_buffer, padding); SSH_LOG(SSH_LOG_PACKET, "The packet is valid"); /* TODO FIXME #ifdef WITH_ZLIB 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(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(SSH_LOG_PACKET, "Nested packet processing. Delaying."); return 0; } error: session->session_state=SSH_SESSION_STATE_ERROR; return processed; }
/** \internal * \brief starts a nonblocking flush of the output buffer * */ int ssh_socket_nonblocking_flush(ssh_socket s) { ssh_session session = s->session; uint32_t len; int w; if (!ssh_socket_is_open(s)) { session->alive = 0; if(s->callbacks && s->callbacks->exception){ s->callbacks->exception( SSH_SOCKET_EXCEPTION_ERROR, s->last_errno,s->callbacks->userdata); }else{ ssh_set_error(session, SSH_FATAL, "Writing packet: error on socket (or connection closed): %s", strerror(s->last_errno)); } return SSH_ERROR; } len = ssh_buffer_get_len(s->out_buffer); if (!s->write_wontblock && s->poll_out && len > 0) { /* force the poll system to catch pollout events */ ssh_poll_add_events(s->poll_out, POLLOUT); return SSH_AGAIN; } if (s->write_wontblock && len > 0) { w = ssh_socket_unbuffered_write(s, ssh_buffer_get(s->out_buffer), len); if (w < 0) { session->alive = 0; ssh_socket_close(s); if(s->callbacks && s->callbacks->exception){ s->callbacks->exception( SSH_SOCKET_EXCEPTION_ERROR, s->last_errno,s->callbacks->userdata); }else{ ssh_set_error(session, SSH_FATAL, "Writing packet: error on socket (or connection closed): %s", strerror(s->last_errno)); } return SSH_ERROR; } ssh_buffer_pass_bytes(s->out_buffer, w); if (s->session->socket_counter != NULL) { s->session->socket_counter->out_bytes += w; } } /* Is there some data pending? */ len = ssh_buffer_get_len(s->out_buffer); if (s->poll_out && len > 0) { /* force the poll system to catch pollout events */ ssh_poll_add_events(s->poll_out, POLLOUT); return SSH_AGAIN; } /* all data written */ return SSH_OK; }
/** @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); unsigned int lenfield_blocksize = (session->current_crypto ? session->current_crypto->in_cipher->lenfield_blocksize : 8); size_t current_macsize = 0; uint8_t *ptr = NULL; int to_be_read; int rc; uint8_t *cleartext_packet = NULL; uint8_t *packet_second_block = NULL; uint8_t *mac = NULL; size_t packet_remaining; uint32_t packet_len, compsize, payloadsize; uint8_t padding; size_t processed = 0; /* number of byte processed from the callback */ if(session->current_crypto != NULL) { current_macsize = hmac_digest_len(session->current_crypto->in_hmac); } if (lenfield_blocksize == 0) { lenfield_blocksize = blocksize; } if (data == NULL) { goto error; } if (session->session_state == SSH_SESSION_STATE_ERROR) { goto error; } #ifdef DEBUG_PACKET SSH_LOG(SSH_LOG_PACKET, "rcv packet cb (len=%zu, state=%s)", receivedlen, session->packet_state == PACKET_STATE_INIT ? "INIT" : session->packet_state == PACKET_STATE_SIZEREAD ? "SIZE_READ" : session->packet_state == PACKET_STATE_PROCESSING ? "PROCESSING" : "unknown"); #endif switch(session->packet_state) { case PACKET_STATE_INIT: if (receivedlen < lenfield_blocksize) { /* * We didn't receive enough data to read at least one * block size, give up */ #ifdef DEBUG_PACKET SSH_LOG(SSH_LOG_PACKET, "Waiting for more data (%zu < %u)", receivedlen, lenfield_blocksize); #endif return 0; } memset(&session->in_packet, 0, sizeof(PACKET)); if (session->in_buffer) { rc = ssh_buffer_reinit(session->in_buffer); if (rc < 0) { goto error; } } else { session->in_buffer = ssh_buffer_new(); if (session->in_buffer == NULL) { goto error; } } ptr = ssh_buffer_allocate(session->in_buffer, lenfield_blocksize); if (ptr == NULL) { goto error; } processed += lenfield_blocksize; packet_len = ssh_packet_decrypt_len(session, ptr, (uint8_t *)data); if (packet_len > MAX_PACKET_LEN) { ssh_set_error(session, SSH_FATAL, "read_packet(): Packet len too high(%u %.4x)", packet_len, packet_len); goto error; } to_be_read = packet_len - lenfield_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; } session->in_packet.len = packet_len; session->packet_state = PACKET_STATE_SIZEREAD; FALL_THROUGH; case PACKET_STATE_SIZEREAD: packet_len = session->in_packet.len; processed = lenfield_blocksize; to_be_read = packet_len + sizeof(uint32_t) + current_macsize; /* if to_be_read is zero, the whole packet was blocksize bytes. */ if (to_be_read != 0) { if (receivedlen < (unsigned int)to_be_read) { /* give up, not enough data in buffer */ SSH_LOG(SSH_LOG_PACKET, "packet: partial packet (read len) " "[len=%d, receivedlen=%d, to_be_read=%d]", packet_len, (int)receivedlen, to_be_read); return 0; } packet_second_block = (uint8_t*)data + lenfield_blocksize; processed = to_be_read - current_macsize; } /* remaining encrypted bytes from the packet, MAC not included */ packet_remaining = packet_len - (lenfield_blocksize - sizeof(uint32_t)); cleartext_packet = ssh_buffer_allocate(session->in_buffer, packet_remaining); if (session->current_crypto) { /* * Decrypt the rest of the packet (lenfield_blocksize bytes already * have been decrypted) */ if (packet_remaining > 0) { rc = ssh_packet_decrypt(session, cleartext_packet, (uint8_t *)data, lenfield_blocksize, processed - lenfield_blocksize); if (rc < 0) { ssh_set_error(session, SSH_FATAL, "Decryption error"); goto error; } } mac = packet_second_block + packet_remaining; rc = ssh_packet_hmac_verify(session, session->in_buffer, mac, session->current_crypto->in_hmac); if (rc < 0) { ssh_set_error(session, SSH_FATAL, "HMAC error"); goto error; } processed += current_macsize; } else { memcpy(cleartext_packet, packet_second_block, packet_remaining); } /* skip the size field which has been processed before */ ssh_buffer_pass_bytes(session->in_buffer, sizeof(uint32_t)); rc = ssh_buffer_get_u8(session->in_buffer, &padding); if (rc == 0) { ssh_set_error(session, SSH_FATAL, "Packet too short to read padding"); goto error; } if (padding > ssh_buffer_get_len(session->in_buffer)) { ssh_set_error(session, SSH_FATAL, "Invalid padding: %d (%d left)", padding, ssh_buffer_get_len(session->in_buffer)); goto error; } ssh_buffer_pass_bytes_end(session->in_buffer, padding); compsize = ssh_buffer_get_len(session->in_buffer); #ifdef WITH_ZLIB if (session->current_crypto && session->current_crypto->do_compress_in && ssh_buffer_get_len(session->in_buffer) > 0) { rc = decompress_buffer(session, session->in_buffer,MAX_PACKET_LEN); if (rc < 0) { goto error; } } #endif /* WITH_ZLIB */ payloadsize = ssh_buffer_get_len(session->in_buffer); session->recv_seq++; if (session->raw_counter != NULL) { session->raw_counter->in_bytes += payloadsize; session->raw_counter->in_packets++; } /* * 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, packet_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); ptr = ((uint8_t*)data) + processed; rc = ssh_packet_socket_callback(ptr, receivedlen - processed,user); processed += rc; } return processed; case PACKET_STATE_PROCESSING: SSH_LOG(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: session->session_state= SSH_SESSION_STATE_ERROR; SSH_LOG(SSH_LOG_PACKET,"Packet: processed %" PRIdS " bytes", processed); return processed; }
/** * @brief SSH poll callback. This callback will be used when an event * caught on the socket. * * @param p Poll object this callback belongs to. * @param fd The raw socket. * @param revents The current poll events on the socket. * @param userdata Userdata to be passed to the callback function, * in this case the socket object. * * @return 0 on success, < 0 when the poll object has been removed * from its poll context. */ int ssh_socket_pollcallback(struct ssh_poll_handle_struct *p, socket_t fd, int revents, void *v_s) { ssh_socket s = (ssh_socket)v_s; char buffer[MAX_BUF_SIZE]; int r; int err = 0; socklen_t errlen = sizeof(err); /* Do not do anything if this socket was already closed */ if (!ssh_socket_is_open(s)) { return -1; } SSH_LOG(SSH_LOG_TRACE, "Poll callback on socket %d (%s%s%s), out buffer %d",fd, (revents & POLLIN) ? "POLLIN ":"", (revents & POLLOUT) ? "POLLOUT ":"", (revents & POLLERR) ? "POLLERR":"", ssh_buffer_get_len(s->out_buffer)); if (revents & POLLERR || revents & POLLHUP) { /* Check if we are in a connecting state */ if (s->state == SSH_SOCKET_CONNECTING) { s->state = SSH_SOCKET_ERROR; r = getsockopt(fd, SOL_SOCKET, SO_ERROR, (char *)&err, &errlen); if (r < 0) { err = errno; } s->last_errno = err; ssh_socket_close(s); if (s->callbacks && s->callbacks->connected) { s->callbacks->connected(SSH_SOCKET_CONNECTED_ERROR, err, s->callbacks->userdata); } return -1; } /* Then we are in a more standard kind of error */ /* force a read to get an explanation */ revents |= POLLIN; } if ((revents & POLLIN) && s->state == SSH_SOCKET_CONNECTED) { s->read_wontblock = 1; r = ssh_socket_unbuffered_read(s, buffer, sizeof(buffer)); if (r < 0) { if (p != NULL) { ssh_poll_remove_events(p, POLLIN); } if (s->callbacks && s->callbacks->exception) { s->callbacks->exception(SSH_SOCKET_EXCEPTION_ERROR, s->last_errno, s->callbacks->userdata); /* p may have been freed, so don't use it * anymore in this function */ p = NULL; return -2; } } if (r == 0) { if (p != NULL) { ssh_poll_remove_events(p, POLLIN); } if (p != NULL) { ssh_poll_remove_events(p, POLLIN); } if (s->callbacks && s->callbacks->exception) { s->callbacks->exception(SSH_SOCKET_EXCEPTION_EOF, 0, s->callbacks->userdata); /* p may have been freed, so don't use it * anymore in this function */ p = NULL; return -2; } } if (r > 0) { if (s->session->socket_counter != NULL) { s->session->socket_counter->in_bytes += r; } /* Bufferize the data and then call the callback */ r = ssh_buffer_add_data(s->in_buffer, buffer, r); if (r < 0) { return -1; } if (s->callbacks && s->callbacks->data) { do { r = s->callbacks->data(ssh_buffer_get(s->in_buffer), ssh_buffer_get_len(s->in_buffer), s->callbacks->userdata); ssh_buffer_pass_bytes(s->in_buffer, r); } while ((r > 0) && (s->state == SSH_SOCKET_CONNECTED)); /* p may have been freed, so don't use it * anymore in this function */ p = NULL; } } } #ifdef _WIN32 if (revents & POLLOUT || revents & POLLWRNORM) { #else if (revents & POLLOUT) { #endif /* First, POLLOUT is a sign we may be connected */ if (s->state == SSH_SOCKET_CONNECTING) { SSH_LOG(SSH_LOG_PACKET, "Received POLLOUT in connecting state"); s->state = SSH_SOCKET_CONNECTED; if (p != NULL) { ssh_poll_set_events(p, POLLOUT | POLLIN); } r = ssh_socket_set_blocking(ssh_socket_get_fd_in(s)); if (r < 0) { return -1; } if (s->callbacks && s->callbacks->connected) { s->callbacks->connected(SSH_SOCKET_CONNECTED_OK, 0, s->callbacks->userdata); } return 0; } /* So, we can write data */ s->write_wontblock=1; if (p != NULL) { ssh_poll_remove_events(p, POLLOUT); } /* If buffered data is pending, write it */ if (ssh_buffer_get_len(s->out_buffer) > 0) { ssh_socket_nonblocking_flush(s); } else if (s->callbacks && s->callbacks->controlflow) { /* Otherwise advertise the upper level that write can be done */ SSH_LOG(SSH_LOG_TRACE,"sending control flow event"); s->callbacks->controlflow(SSH_SOCKET_FLOW_WRITEWONTBLOCK, s->callbacks->userdata); } /* TODO: Find a way to put back POLLOUT when buffering occurs */ } /* Return -1 if one of the poll handlers disappeared */ return (s->poll_in == NULL || s->poll_out == NULL) ? -1 : 0; } /** @internal * @brief returns the input poll handle corresponding to the socket, * creates it if it does not exist. * @returns allocated and initialized ssh_poll_handle object */ ssh_poll_handle ssh_socket_get_poll_handle_in(ssh_socket s){ if(s->poll_in) return s->poll_in; s->poll_in=ssh_poll_new(s->fd_in,0,ssh_socket_pollcallback,s); if(s->fd_in == s->fd_out && s->poll_out == NULL) s->poll_out=s->poll_in; return s->poll_in; } /** @internal * @brief returns the output poll handle corresponding to the socket, * creates it if it does not exist. * @returns allocated and initialized ssh_poll_handle object */ ssh_poll_handle ssh_socket_get_poll_handle_out(ssh_socket s){ if(s->poll_out) return s->poll_out; s->poll_out=ssh_poll_new(s->fd_out,0,ssh_socket_pollcallback,s); if(s->fd_in == s->fd_out && s->poll_in == NULL) s->poll_in=s->poll_out; return s->poll_out; } /** \internal * \brief Deletes a socket object */ void ssh_socket_free(ssh_socket s){ if (s == NULL) { return; } ssh_socket_close(s); ssh_buffer_free(s->in_buffer); ssh_buffer_free(s->out_buffer); SAFE_FREE(s); } #ifndef _WIN32 int ssh_socket_unix(ssh_socket s, const char *path) { struct sockaddr_un sunaddr; socket_t fd; sunaddr.sun_family = AF_UNIX; snprintf(sunaddr.sun_path, sizeof(sunaddr.sun_path), "%s", path); fd = socket(AF_UNIX, SOCK_STREAM, 0); if (fd == SSH_INVALID_SOCKET) { ssh_set_error(s->session, SSH_FATAL, "Error from socket(AF_UNIX, SOCK_STREAM, 0): %s", strerror(errno)); return -1; } if (fcntl(fd, F_SETFD, 1) == -1) { ssh_set_error(s->session, SSH_FATAL, "Error from fcntl(fd, F_SETFD, 1): %s", strerror(errno)); close(fd); return -1; } if (connect(fd, (struct sockaddr *) &sunaddr, sizeof(sunaddr)) < 0) { ssh_set_error(s->session, SSH_FATAL, "Error from connect(): %s", strerror(errno)); close(fd); return -1; } ssh_socket_set_fd(s,fd); return 0; } #endif /** \internal * \brief closes a socket */ void ssh_socket_close(ssh_socket s){ if (ssh_socket_is_open(s)) { #ifdef _WIN32 CLOSE_SOCKET(s->fd_in); /* fd_in = fd_out under win32 */ s->last_errno = WSAGetLastError(); #else if (s->fd_out != s->fd_in && s->fd_out != -1) { CLOSE_SOCKET(s->fd_out); } CLOSE_SOCKET(s->fd_in); s->last_errno = errno; #endif } if(s->poll_in != NULL){ if(s->poll_out == s->poll_in) s->poll_out = NULL; ssh_poll_free(s->poll_in); s->poll_in=NULL; } if(s->poll_out != NULL){ ssh_poll_free(s->poll_out); s->poll_out=NULL; } s->state = SSH_SOCKET_CLOSED; } /** * @internal * @brief sets the file descriptor of the socket. * @param[out] s ssh_socket to update * @param[in] fd file descriptor to set * @warning this function updates boths the input and output * file descriptors */ void ssh_socket_set_fd(ssh_socket s, socket_t fd) { s->fd_in = s->fd_out = fd; if (s->poll_in) { ssh_poll_set_fd(s->poll_in,fd); } else { s->state = SSH_SOCKET_CONNECTING; /* POLLOUT is the event to wait for in a nonblocking connect */ ssh_poll_set_events(ssh_socket_get_poll_handle_in(s), POLLOUT); #ifdef _WIN32 ssh_poll_add_events(ssh_socket_get_poll_handle_in(s), POLLWRNORM); #endif } }
/** @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); unsigned char mac[DIGEST_MAX_LEN] = {0}; char buffer[16] = {0}; size_t current_macsize = 0; const uint8_t *packet; 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(session->current_crypto != NULL) { current_macsize = hmac_digest_len(session->current_crypto->in_hmac); } 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) { rc = ssh_buffer_reinit(session->in_buffer); if (rc < 0) { goto error; } } else { session->in_buffer = ssh_buffer_new(); if (session->in_buffer == NULL) { goto error; } } memcpy(buffer, data, blocksize); processed += blocksize; len = ssh_packet_decrypt_len(session, buffer); rc = ssh_buffer_add_data(session->in_buffer, buffer, blocksize); if (rc < 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 = ((uint8_t*)data) + processed; #if 0 ssh_socket_read(session->socket, packet, to_be_read - current_macsize); #endif rc = ssh_buffer_add_data(session->in_buffer, packet, to_be_read - current_macsize); if (rc < 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) */ uint32_t buffer_len = ssh_buffer_get_len(session->in_buffer); /* The following check avoids decrypting zero bytes */ if (buffer_len > blocksize) { uint8_t *payload = ((uint8_t*)ssh_buffer_get(session->in_buffer) + blocksize); uint32_t plen = buffer_len - blocksize; rc = ssh_packet_decrypt(session, payload, plen); if (rc < 0) { ssh_set_error(session, SSH_FATAL, "Decrypt error"); goto error; } } /* copy the last part from the incoming buffer */ packet = ((uint8_t *)data) + processed; memcpy(mac, packet, current_macsize); rc = ssh_packet_hmac_verify(session, session->in_buffer, mac, session->current_crypto->in_hmac); if (rc < 0) { ssh_set_error(session, SSH_FATAL, "HMAC error"); goto error; } processed += current_macsize; } /* skip the size field which has been processed before */ ssh_buffer_pass_bytes(session->in_buffer, sizeof(uint32_t)); rc = ssh_buffer_get_u8(session->in_buffer, &padding); if (rc == 0) { ssh_set_error(session, SSH_FATAL, "Packet too short to read padding"); goto error; } if (padding > ssh_buffer_get_len(session->in_buffer)) { ssh_set_error(session, SSH_FATAL, "Invalid padding: %d (%d left)", padding, ssh_buffer_get_len(session->in_buffer)); goto error; } ssh_buffer_pass_bytes_end(session->in_buffer, padding); compsize = ssh_buffer_get_len(session->in_buffer); #ifdef WITH_ZLIB if (session->current_crypto && session->current_crypto->do_compress_in && ssh_buffer_get_len(session->in_buffer) > 0) { rc = decompress_buffer(session, session->in_buffer,MAX_PACKET_LEN); if (rc < 0) { goto error; } } #endif /* WITH_ZLIB */ payloadsize = ssh_buffer_get_len(session->in_buffer); session->recv_seq++; if (session->raw_counter != NULL) { session->raw_counter->in_bytes += payloadsize; session->raw_counter->in_packets++; } /* * 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); packet = ((uint8_t*)data) + processed; rc = ssh_packet_socket_callback(packet, receivedlen - processed,user); processed += rc; } return processed; case PACKET_STATE_PROCESSING: SSH_LOG(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: session->session_state= SSH_SESSION_STATE_ERROR; return processed; }