Exemple #1
0
/* 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;
}
Exemple #2
0
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;
}
Exemple #3
0
/** @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;
}