Exemple #1
0
int
_gnutls_epoch_set_cipher_suite (gnutls_session_t session,
                                int epoch_rel, cipher_suite_st * suite)
{
  gnutls_cipher_algorithm_t cipher_algo;
  gnutls_mac_algorithm_t mac_algo;
  record_parameters_st *params;
  int ret;

  ret = _gnutls_epoch_get (session, epoch_rel, &params);
  if (ret < 0)
    return gnutls_assert_val (ret);

  if (params->initialized
      || params->cipher_algorithm != GNUTLS_CIPHER_UNKNOWN
      || params->mac_algorithm != GNUTLS_MAC_UNKNOWN)
    return gnutls_assert_val (GNUTLS_E_INTERNAL_ERROR);

  cipher_algo = _gnutls_cipher_suite_get_cipher_algo (suite);
  mac_algo = _gnutls_cipher_suite_get_mac_algo (suite);

  if (_gnutls_cipher_is_ok (cipher_algo) != 0
      || _gnutls_mac_is_ok (mac_algo) != 0)
    return gnutls_assert_val (GNUTLS_E_UNWANTED_ALGORITHM);

  params->cipher_algorithm = cipher_algo;
  params->mac_algorithm = mac_algo;

  return 0;
}
Exemple #2
0
int
_gnutls_epoch_set_compression(gnutls_session_t session,
			      int epoch_rel,
			      gnutls_compression_method_t comp_algo)
{
	record_parameters_st *params;
	int ret;

	ret = _gnutls_epoch_get(session, epoch_rel, &params);
	if (ret < 0)
		return gnutls_assert_val(ret);

	if (params->initialized
	    || params->compression_algorithm != GNUTLS_COMP_UNKNOWN)
		return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);

	if (_gnutls_compression_is_ok(comp_algo) != 0)
		return
		    gnutls_assert_val
		    (GNUTLS_E_UNKNOWN_COMPRESSION_ALGORITHM);

	params->compression_algorithm = comp_algo;

	return 0;
}
Exemple #3
0
int
_gnutls_epoch_set_keys (gnutls_session_t session, uint16_t epoch)
{
  int hash_size;
  int IV_size;
  int key_size, export_flag;
  gnutls_cipher_algorithm_t cipher_algo;
  gnutls_mac_algorithm_t mac_algo;
  gnutls_compression_method_t comp_algo;
  record_parameters_st *params;
  int ret;
  gnutls_protocol_t ver = gnutls_protocol_get_version (session);

  ret = _gnutls_epoch_get (session, epoch, &params);
  if (ret < 0)
    return gnutls_assert_val (ret);

  if (params->initialized)
    return 0;

  _gnutls_record_log
    ("REC[%p]: Initializing epoch #%u\n", session, params->epoch);

  cipher_algo = params->cipher_algorithm;
  mac_algo = params->mac_algorithm;
  comp_algo = params->compression_algorithm;

  if (_gnutls_cipher_is_ok (cipher_algo) != 0
      || _gnutls_mac_is_ok (mac_algo) != 0)
    return gnutls_assert_val (GNUTLS_E_INTERNAL_ERROR);

  if (_gnutls_compression_is_ok (comp_algo) != 0)
    return gnutls_assert_val (GNUTLS_E_UNKNOWN_COMPRESSION_ALGORITHM);

  IV_size = _gnutls_cipher_get_iv_size (cipher_algo);
  key_size = gnutls_cipher_get_key_size (cipher_algo);
  export_flag = _gnutls_cipher_get_export_flag (cipher_algo);
  hash_size = _gnutls_hmac_get_algo_len (mac_algo);

  ret = _gnutls_set_keys
    (session, params, hash_size, IV_size, key_size, export_flag);
  if (ret < 0)
    return gnutls_assert_val (ret);

  ret = _gnutls_init_record_state (params, ver, 1, &params->read);
  if (ret < 0)
    return gnutls_assert_val (ret);

  ret = _gnutls_init_record_state (params, ver, 0, &params->write);
  if (ret < 0)
    return gnutls_assert_val (ret);

  params->record_sw_size = 0;

  _gnutls_record_log ("REC[%p]: Epoch #%u ready\n", session, params->epoch);

  params->initialized = 1;
  return 0;
}
Exemple #4
0
int _gnutls_epoch_get_compression(gnutls_session_t session, int epoch)
{
record_parameters_st *params;
int ret;

  ret = _gnutls_epoch_get (session, epoch, &params);
  if (ret < 0)
    return GNUTLS_COMP_UNKNOWN;

  return params->compression_algorithm;
}
Exemple #5
0
inline static int
is_read_comp_null (gnutls_session_t session)
{
  record_parameters_st *record_params;

  _gnutls_epoch_get (session, EPOCH_READ_CURRENT, &record_params);
  if (record_params->compression_algorithm == GNUTLS_COMP_NULL)
    return 0;

  return 1;
}
Exemple #6
0
/**
 * gnutls_compression_get:
 * @session: is a #gnutls_session_t structure.
 *
 * Get currently used compression algorithm.
 *
 * Returns: the currently used compression method, a
 *   #gnutls_compression_method_t value.
 **/
gnutls_compression_method_t
gnutls_compression_get (gnutls_session_t session)
{
  record_parameters_st *record_params;
  int ret;
  
  ret = _gnutls_epoch_get (session, EPOCH_READ_CURRENT, &record_params);
  if (ret < 0)
    return gnutls_assert_val(GNUTLS_COMP_NULL);

  return record_params->compression_algorithm;
}
Exemple #7
0
/**
 * gnutls_mac_get:
 * @session: is a #gnutls_session_t structure.
 *
 * Get currently used MAC algorithm.
 *
 * Returns: the currently used mac algorithm, a
 *   #gnutls_mac_algorithm_t value.
 **/
gnutls_mac_algorithm_t
gnutls_mac_get (gnutls_session_t session)
{
  record_parameters_st *record_params;
  int ret;
  
  ret = _gnutls_epoch_get (session, EPOCH_READ_CURRENT, &record_params);
  if (ret < 0)
    return gnutls_assert_val(GNUTLS_MAC_NULL);

  return record_params->mac_algorithm;
}
/**
 * gnutls_cipher_get:
 * @session: is a #gnutls_session_t structure.
 *
 * Get currently used cipher.
 *
 * Returns: the currently used cipher, a #gnutls_cipher_algorithm_t
 *   type.
 **/
gnutls_cipher_algorithm_t gnutls_cipher_get(gnutls_session_t session)
{
	record_parameters_st *record_params;
	int ret;

	ret =
	    _gnutls_epoch_get(session, EPOCH_READ_CURRENT, &record_params);
	if (ret < 0)
		return gnutls_assert_val(GNUTLS_CIPHER_NULL);

	return record_params->cipher->id;
}
Exemple #9
0
/* returns overhead imposed by the record layer (encryption/compression)
 * etc. It does not include the record layer headers, since the caller
 * needs to cope with rounding to multiples of blocksize, and the header
 * is outside that.
 *
 * blocksize: will contain the block size when padding may be required or 1
 *
 * It may return a negative error code on error.
 */
static int record_overhead_rt(gnutls_session_t session, unsigned int *blocksize)
{
record_parameters_st *params;
int total = 0, ret, iv_size;

  if (session->internals.initial_negotiation_completed == 0)
    return GNUTLS_E_INVALID_REQUEST;

  ret = _gnutls_epoch_get (session, EPOCH_WRITE_CURRENT, &params);
  if (ret < 0)
    return gnutls_assert_val(ret);

  /* requires padding */
  iv_size = _gnutls_cipher_get_iv_size(params->cipher_algorithm);

  if (_gnutls_cipher_is_block (params->cipher_algorithm) == CIPHER_BLOCK)
    {
      *blocksize = iv_size;

      total += iv_size; /* iv_size == block_size in DTLS */

      /* We always pad with at least one byte; never 0. */
      total++;
    }
  else
    {
      *blocksize = 1;
    }
  
  if (params->mac_algorithm == GNUTLS_MAC_AEAD)
    total += _gnutls_cipher_get_tag_size(params->cipher_algorithm);
  else
    {
      ret = _gnutls_hmac_get_algo_len(params->mac_algorithm);
      if (ret < 0)
        return gnutls_assert_val(ret);
      total+=ret;
    }

  if (params->compression_algorithm != GNUTLS_COMP_NULL)
    total += EXTRA_COMP_SIZE;

  return total;
}
Exemple #10
0
/**
 * gnutls_dtls_prestate_set:
 * @session: a new session
 * @prestate: contains the client's prestate
 *
 * This function will associate the prestate acquired by
 * the cookie authentication with the client, with the newly 
 * established session.
 *
 * Since: 3.0
 **/
void gnutls_dtls_prestate_set(gnutls_session_t session, gnutls_dtls_prestate_st* prestate)
{
  record_parameters_st *params;
  int ret;

  if (prestate == NULL)
    return;

  /* we do not care about read_params, since we accept anything
   * the peer sends.
   */
  ret = _gnutls_epoch_get (session, EPOCH_WRITE_CURRENT, &params);
  if (ret < 0)
    return;

  params->write.sequence_number.i[7] = prestate->record_seq;

  session->internals.dtls.hsk_read_seq = prestate->hsk_read_seq;
  session->internals.dtls.hsk_write_seq = prestate->hsk_write_seq + 1;
}
Exemple #11
0
int
_gnutls_epoch_set_cipher_suite(gnutls_session_t session,
			       int epoch_rel, const uint8_t suite[2])
{
	const cipher_entry_st *cipher_algo;
	const mac_entry_st *mac_algo;
	record_parameters_st *params;
	const gnutls_cipher_suite_entry_st *cs;
	int ret;

	ret = _gnutls_epoch_get(session, epoch_rel, &params);
	if (ret < 0)
		return gnutls_assert_val(ret);

	if (params->initialized
	    || params->cipher != NULL || params->mac != NULL)
		return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);

	cs = ciphersuite_to_entry(suite);
	if (cs == NULL)
		return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);

	cipher_algo = cipher_to_entry(cs->block_algorithm);
	mac_algo = mac_to_entry(cs->mac_algorithm);

	if (_gnutls_cipher_is_ok(cipher_algo) == 0
	    || _gnutls_mac_is_ok(mac_algo) == 0)
		return gnutls_assert_val(GNUTLS_E_UNWANTED_ALGORITHM);

	if (_gnutls_cipher_priority(session, cipher_algo->id) < 0)
		return gnutls_assert_val(GNUTLS_E_UNWANTED_ALGORITHM);

	if (_gnutls_mac_priority(session, mac_algo->id) < 0)
		return gnutls_assert_val(GNUTLS_E_UNWANTED_ALGORITHM);

	params->cipher = cipher_algo;
	params->mac = mac_algo;

	return 0;
}
Exemple #12
0
/* Format: 
 *      4 bytes the total security data size
 *      1 byte the entity type (client/server)
 *      1 byte the key exchange algorithm used
 *      1 byte the read cipher algorithm
 *      1 byte the read mac algorithm
 *      1 byte the read compression algorithm
 *
 *      1 byte the write cipher algorithm
 *      1 byte the write mac algorithm
 *      1 byte the write compression algorithm
 *
 *      1 byte the certificate type
 *      1 byte the protocol version
 *
 *      2 bytes the cipher suite
 *
 *      48 bytes the master secret
 *
 *      32 bytes the client random
 *      32 bytes the server random
 *
 *      1 byte the session ID size
 *      x bytes the session ID (32 bytes max)
 *
 *      4 bytes a timestamp
 *            -------------------
 *                MAX: 165 bytes
 *
 */
static int
pack_security_parameters (gnutls_session_t session, gnutls_buffer_st * ps)
{

  int ret;
  int size_offset;
  size_t cur_size;
  record_parameters_st *params;

  if (session->security_parameters.epoch_read
      != session->security_parameters.epoch_write)
    {
      gnutls_assert ();
      return GNUTLS_E_INVALID_REQUEST;
    }

  ret = _gnutls_epoch_get (session, EPOCH_READ_CURRENT, &params);
  if (ret < 0)
    {
      gnutls_assert ();
      return ret;
    }

  /* move after the auth info stuff.
   */
  size_offset = ps->length;
  BUFFER_APPEND_NUM (ps, 0);
  cur_size = ps->length;


  BUFFER_APPEND (ps, &session->security_parameters.entity, 1);
  BUFFER_APPEND (ps, &session->security_parameters.kx_algorithm, 1);
  BUFFER_APPEND (ps,
                 &session->security_parameters.current_cipher_suite.suite[0],
                 1);
  BUFFER_APPEND (ps,
                 &session->security_parameters.current_cipher_suite.suite[1],
                 1);
  BUFFER_APPEND (ps, &params->compression_algorithm, 1);
  BUFFER_APPEND (ps, &session->security_parameters.cert_type, 1);
  BUFFER_APPEND (ps, &session->security_parameters.version, 1);

  BUFFER_APPEND (ps, session->security_parameters.master_secret,
                 GNUTLS_MASTER_SIZE);
  BUFFER_APPEND (ps, session->security_parameters.client_random,
                 GNUTLS_RANDOM_SIZE);
  BUFFER_APPEND (ps, session->security_parameters.server_random,
                 GNUTLS_RANDOM_SIZE);

  BUFFER_APPEND_NUM (ps, session->security_parameters.session_id_size);
  BUFFER_APPEND (ps, session->security_parameters.session_id,
                 session->security_parameters.session_id_size);

  BUFFER_APPEND_NUM (ps, session->security_parameters.max_record_send_size);
  BUFFER_APPEND_NUM (ps, session->security_parameters.max_record_recv_size);
  BUFFER_APPEND_NUM (ps, session->security_parameters.timestamp);

  _gnutls_write_uint32 (ps->length - cur_size, ps->data + size_offset);

  return 0;
}
Exemple #13
0
int _gnutls_epoch_set_keys(gnutls_session_t session, uint16_t epoch)
{
	int hash_size;
	int IV_size;
	int key_size;
	gnutls_compression_method_t comp_algo;
	record_parameters_st *params;
	int ret;
	const version_entry_st *ver = get_version(session);

	if (unlikely(ver == NULL))
		return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);

	ret = _gnutls_epoch_get(session, epoch, &params);
	if (ret < 0)
		return gnutls_assert_val(ret);

	if (params->initialized)
		return 0;

	_gnutls_record_log
	    ("REC[%p]: Initializing epoch #%u\n", session, params->epoch);

	comp_algo = params->compression_algorithm;

	if (_gnutls_cipher_is_ok(params->cipher) == 0
	    || _gnutls_mac_is_ok(params->mac) == 0)
		return gnutls_assert_val(GNUTLS_E_UNWANTED_ALGORITHM);

	if (_gnutls_cipher_priority(session, params->cipher->id) < 0)
		return gnutls_assert_val(GNUTLS_E_UNWANTED_ALGORITHM);

	if (_gnutls_mac_priority(session, params->mac->id) < 0)
		return gnutls_assert_val(GNUTLS_E_UNWANTED_ALGORITHM);

	if (_gnutls_compression_is_ok(comp_algo) != 0)
		return
		    gnutls_assert_val
		    (GNUTLS_E_UNKNOWN_COMPRESSION_ALGORITHM);

	if (!_gnutls_version_has_explicit_iv(ver) &&
	    _gnutls_cipher_type(params->cipher) == CIPHER_BLOCK) {
		IV_size = _gnutls_cipher_get_iv_size(params->cipher);
	} else {
		IV_size = _gnutls_cipher_get_implicit_iv_size(params->cipher);
	}

	key_size = _gnutls_cipher_get_key_size(params->cipher);
	hash_size = _gnutls_mac_get_key_size(params->mac);
	params->etm = session->security_parameters.etm;

	ret = _gnutls_set_keys
	    (session, params, hash_size, IV_size, key_size);
	if (ret < 0)
		return gnutls_assert_val(ret);

	ret = _gnutls_init_record_state(params, ver, 1, &params->read);
	if (ret < 0)
		return gnutls_assert_val(ret);

	ret = _gnutls_init_record_state(params, ver, 0, &params->write);
	if (ret < 0)
		return gnutls_assert_val(ret);

	params->record_sw_size = 0;

	_gnutls_record_log("REC[%p]: Epoch #%u ready\n", session,
			   params->epoch);

	params->initialized = 1;
	return 0;
}
Exemple #14
0
/* This function behaves exactly like write(). The only difference is
 * that it accepts, the gnutls_session_t and the content_type_t of data to
 * send (if called by the user the Content is specific)
 * It is intended to transfer data, under the current session.    
 *
 * Oct 30 2001: Removed capability to send data more than MAX_RECORD_SIZE.
 * This makes the function much easier to read, and more error resistant
 * (there were cases were the old function could mess everything up).
 * --nmav
 *
 * This function may accept a NULL pointer for data, and 0 for size, if
 * and only if the previous send was interrupted for some reason.
 *
 */
ssize_t
_gnutls_send_int (gnutls_session_t session, content_type_t type,
                  gnutls_handshake_description_t htype,
                  unsigned int epoch_rel, const void *_data,
                  size_t sizeofdata, unsigned int mflags)
{
  mbuffer_st *bufel;
  ssize_t cipher_size;
  int retval, ret;
  int data2send_size;
  uint8_t headers[5];
  const uint8_t *data = _data;
  record_parameters_st *record_params;
  record_state_st *record_state;

  ret = _gnutls_epoch_get (session, epoch_rel, &record_params);
  if (ret < 0)
    {
      gnutls_assert ();
      return ret;
    }

  /* Safeguard against processing data with an incomplete cipher state. */
  if (!record_params->initialized)
    {
      gnutls_assert ();
      return GNUTLS_E_INVALID_REQUEST;
    }

  record_state = &record_params->write;

  /* Do not allow null pointer if the send buffer is empty.
   * If the previous send was interrupted then a null pointer is
   * ok, and means to resume.
   */
  if (session->internals.record_send_buffer.byte_length == 0 &&
      (sizeofdata == 0 && _data == NULL))
    {
      gnutls_assert ();
      return GNUTLS_E_INVALID_REQUEST;
    }

  if (type != GNUTLS_ALERT)     /* alert messages are sent anyway */
    if (session_is_valid (session) || session->internals.may_not_write != 0)
      {
        gnutls_assert ();
        return GNUTLS_E_INVALID_SESSION;
      }

  headers[0] = type;

  /* Use the default record version, if it is
   * set.
   */
  copy_record_version (session, htype, &headers[1]);


  _gnutls_record_log
    ("REC[%p]: Sending Packet[%d] %s(%d) with length: %d\n", session,
     (int) _gnutls_uint64touint32 (&record_state->sequence_number),
     _gnutls_packet2str (type), type, (int) sizeofdata);

  if (sizeofdata > MAX_RECORD_SEND_SIZE)
    data2send_size = MAX_RECORD_SEND_SIZE;
  else
    data2send_size = sizeofdata;

  /* Only encrypt if we don't have data to send 
   * from the previous run. - probably interrupted.
   */
  if (mflags != 0 && session->internals.record_send_buffer.byte_length > 0)
    {
      ret = _gnutls_io_write_flush (session);
      if (ret > 0)
        cipher_size = ret;
      else
        cipher_size = 0;

      retval = session->internals.record_send_buffer_user_size;
    }
  else
    {

      /* now proceed to packet encryption
       */
      cipher_size = data2send_size + MAX_RECORD_OVERHEAD;
      bufel = _mbuffer_alloc (cipher_size, cipher_size);
      if (bufel == NULL)
        {
          gnutls_assert ();
          return GNUTLS_E_MEMORY_ERROR;
        }

      cipher_size =
        _gnutls_encrypt (session, headers, RECORD_HEADER_SIZE, data,
                         data2send_size, _mbuffer_get_udata_ptr (bufel),
                         cipher_size, type,
                         (session->internals.priorities.no_padding ==
                          0) ? 1 : 0, record_params);
      if (cipher_size <= 0)
        {
          gnutls_assert ();
          if (cipher_size == 0)
            cipher_size = GNUTLS_E_ENCRYPTION_FAILED;
          gnutls_free (bufel);
          return cipher_size;   /* error */
        }

      retval = data2send_size;
      session->internals.record_send_buffer_user_size = data2send_size;

      /* increase sequence number
       */
      if (_gnutls_uint64pp (&record_state->sequence_number) != 0)
        {
          session_invalidate (session);
          gnutls_assert ();
          gnutls_free (bufel);
          return GNUTLS_E_RECORD_LIMIT_REACHED;
        }

      _mbuffer_set_udata_size (bufel, cipher_size);
      ret = _gnutls_io_write_buffered (session, bufel, mflags);
    }

  if (ret != cipher_size)
    {
      if (ret < 0 && gnutls_error_is_fatal (ret) == 0)
        {
          /* If we have sent any data then just return
           * the error value. Do not invalidate the session.
           */
          gnutls_assert ();
          return ret;
        }

      if (ret > 0)
        {
          gnutls_assert ();
          ret = GNUTLS_E_INTERNAL_ERROR;
        }
      session_unresumable (session);
      session->internals.may_not_write = 1;
      gnutls_assert ();
      return ret;
    }

  session->internals.record_send_buffer_user_size = 0;

  _gnutls_record_log ("REC[%p]: Sent Packet[%d] %s(%d) with length: %d\n",
                      session,
                      (int)
                      _gnutls_uint64touint32
                      (&record_state->sequence_number),
                      _gnutls_packet2str (type), type, (int) cipher_size);

  return retval;
}
Exemple #15
0
/* This function behaves exactly like read(). The only difference is
 * that it accepts the gnutls_session_t and the content_type_t of data to
 * receive (if called by the user the Content is Userdata only)
 * It is intended to receive data, under the current session.
 *
 * The gnutls_handshake_description_t was introduced to support SSL V2.0 client hellos.
 */
ssize_t
_gnutls_recv_int (gnutls_session_t session, content_type_t type,
                  gnutls_handshake_description_t htype,
                  opaque * data, size_t sizeofdata)
{
  int decrypted_length;
  opaque version[2];
  content_type_t recv_type;
  uint16_t length;
  uint8_t *ciphertext;
  int ret, ret2;
  uint16_t header_size;
  int empty_packet = 0;
  gnutls_datum_t data_enc, tmp;
  record_parameters_st *record_params;
  record_state_st *record_state;

  ret = _gnutls_epoch_get (session, EPOCH_READ_CURRENT, &record_params);
  if (ret < 0)
    {
      gnutls_assert ();
      return ret;
    }

  /* Safeguard against processing data with an incomplete cipher state. */
  if (!record_params->initialized)
    {
      gnutls_assert ();
      return GNUTLS_E_INVALID_REQUEST;
    }

  record_state = &record_params->read;

  if (type != GNUTLS_ALERT && (sizeofdata == 0 || data == NULL))
    {
      return GNUTLS_E_INVALID_REQUEST;
    }

begin:

  if (empty_packet > MAX_EMPTY_PACKETS_SEQUENCE)
    {
      gnutls_assert ();
      return GNUTLS_E_TOO_MANY_EMPTY_PACKETS;
    }

  if (session->internals.read_eof != 0)
    {
      /* if we have already read an EOF
       */
      return 0;
    }
  else if (session_is_valid (session) != 0
           || session->internals.may_not_read != 0)
    {
      gnutls_assert ();
      return GNUTLS_E_INVALID_SESSION;
    }

/* If we have enough data in the cache do not bother receiving
 * a new packet. (in order to flush the cache)
 */
  ret = check_buffers (session, type, data, sizeofdata);
  if (ret != 0)
    return ret;


/* default headers for TLS 1.0
 */
  header_size = RECORD_HEADER_SIZE;

  if ((ret =
       _gnutls_io_read_buffered (session, header_size, -1)) != header_size)
    {
      if (ret < 0 && gnutls_error_is_fatal (ret) == 0)
        return ret;

      session_invalidate (session);
      if (type == GNUTLS_ALERT)
        {
          gnutls_assert ();
          return 0;             /* we were expecting close notify */
        }
      session_unresumable (session);
      gnutls_assert ();
      return GNUTLS_E_UNEXPECTED_PACKET_LENGTH;
    }

  ret = _mbuffer_linearize (&session->internals.record_recv_buffer);
  if (ret != 0)
    {
      gnutls_assert ();
      return ret;
    }

  _mbuffer_get_first (&session->internals.record_recv_buffer, &data_enc);

  if ((ret =
       record_check_headers (session, data_enc.data, type, htype, &recv_type,
                             version, &length, &header_size)) < 0)
    {
      gnutls_assert ();
      return ret;
    }

/* Here we check if the Type of the received packet is
 * ok. 
 */
  if ((ret = check_recv_type (recv_type)) < 0)
    {
      gnutls_assert ();
      return ret;
    }

/* Here we check if the advertized version is the one we
 * negotiated in the handshake.
 */
  if ((ret = record_check_version (session, htype, version)) < 0)
    {
      gnutls_assert ();
      session_invalidate (session);
      return ret;
    }

  _gnutls_record_log
    ("REC[%p]: Expected Packet[%d] %s(%d) with length: %d\n", session,
     (int) _gnutls_uint64touint32 (&record_state->sequence_number),
     _gnutls_packet2str (type), type, (int) sizeofdata);
  _gnutls_record_log ("REC[%p]: Received Packet[%d] %s(%d) with length: %d\n",
                      session,
                      (int)
                      _gnutls_uint64touint32 (&record_state->sequence_number),
                      _gnutls_packet2str (recv_type), recv_type, length);

  if (length > MAX_RECV_SIZE)
    {
      _gnutls_record_log
        ("REC[%p]: FATAL ERROR: Received packet with length: %d\n",
         session, length);

      session_unresumable (session);
      session_invalidate (session);
      gnutls_assert ();
      return GNUTLS_E_UNEXPECTED_PACKET_LENGTH;
    }

/* check if we have that data into buffer. 
 */
  if ((ret =
       _gnutls_io_read_buffered (session, header_size + length,
                                 recv_type)) != header_size + length)
    {
      if (ret < 0 && gnutls_error_is_fatal (ret) == 0)
        return ret;

      session_unresumable (session);
      session_invalidate (session);
      gnutls_assert ();
      return GNUTLS_E_UNEXPECTED_PACKET_LENGTH;
    }

/* ok now we are sure that we can read all the data - so
 * move on !
 */

  ret = _mbuffer_linearize (&session->internals.record_recv_buffer);
  if (ret != 0)
    {
      gnutls_assert ();
      return ret;
    }
  _mbuffer_get_first (&session->internals.record_recv_buffer, &data_enc);
  ciphertext = &data_enc.data[header_size];

  ret = get_temp_recv_buffer (session, &tmp);
  if (ret < 0)
    {
      gnutls_assert ();
      return ret;
    }

/* decrypt the data we got. 
 */
  ret =
    _gnutls_decrypt (session, ciphertext, length, tmp.data, tmp.size,
                     recv_type, record_params);
  if (ret < 0)
    {
      session_unresumable (session);
      session_invalidate (session);
      gnutls_assert ();
      return ret;
    }
  _mbuffer_remove_bytes (&session->internals.record_recv_buffer,
                         header_size + length);
  decrypted_length = ret;

/* Check if this is a CHANGE_CIPHER_SPEC
 */
  if (type == GNUTLS_CHANGE_CIPHER_SPEC &&
      recv_type == GNUTLS_CHANGE_CIPHER_SPEC)
    {

      _gnutls_record_log
        ("REC[%p]: ChangeCipherSpec Packet was received\n", session);

      if ((size_t) ret != sizeofdata)
        {                       /* sizeofdata should be 1 */
          gnutls_assert ();
          return GNUTLS_E_UNEXPECTED_PACKET_LENGTH;
        }
      memcpy (data, tmp.data, sizeofdata);

      return ret;
    }

  _gnutls_record_log
    ("REC[%p]: Decrypted Packet[%d] %s(%d) with length: %d\n", session,
     (int) _gnutls_uint64touint32 (&record_state->sequence_number),
     _gnutls_packet2str (recv_type), recv_type, decrypted_length);

/* increase sequence number 
 */
  if (_gnutls_uint64pp (&record_state->sequence_number) != 0)
    {
      session_invalidate (session);
      gnutls_assert ();
      return GNUTLS_E_RECORD_LIMIT_REACHED;
    }

  ret =
    record_check_type (session, recv_type, type, htype, tmp.data,
                       decrypted_length);
  if (ret < 0)
    {
      if (ret == GNUTLS_E_INT_RET_0)
        return 0;
      gnutls_assert ();
      return ret;
    }

/* Get Application data from buffer 
 */
  if ((recv_type == type) &&
      (type == GNUTLS_APPLICATION_DATA ||
       type == GNUTLS_HANDSHAKE || type == GNUTLS_INNER_APPLICATION))
    {

      ret = _gnutls_record_buffer_get (type, session, data, sizeofdata);
      if (ret < 0)
        {
          gnutls_assert ();
          return ret;
        }

      /* if the buffer just got empty 
       */
      if (_gnutls_record_buffer_get_size (type, session) == 0)
        {
          if ((ret2 = _gnutls_io_clear_peeked_data (session)) < 0)
            {
              gnutls_assert ();
              return ret2;
            }
        }
    }
  else
    {
      gnutls_assert ();
      return GNUTLS_E_UNEXPECTED_PACKET;
      /* we didn't get what we wanted to 
       */
    }

/* (originally for) TLS 1.0 CBC protection. 
 * Actually this code is called if we just received
 * an empty packet. An empty TLS packet is usually
 * sent to protect some vulnerabilities in the CBC mode.
 * In that case we go to the beginning and start reading
 * the next packet.
 */
  if (ret == 0)
    {
      empty_packet++;
      goto begin;
    }

  return ret;
}
Exemple #16
0
/* @ms: is the number of milliseconds to wait for data. Use zero for indefinite.
 *
 * This will receive record layer packets and add them to 
 * application_data_buffer and handshake_data_buffer.
 *
 * If the htype is not -1 then handshake timeouts
 * will be enforced.
 */
ssize_t
_gnutls_recv_in_buffers (gnutls_session_t session, content_type_t type,
                         gnutls_handshake_description_t htype, unsigned int ms)
{
  uint64 *packet_sequence;
  gnutls_datum_t ciphertext;
  mbuffer_st* bufel = NULL, *decrypted = NULL;
  gnutls_datum_t t;
  int ret;
  unsigned int empty_fragments = 0;
  record_parameters_st *record_params;
  record_state_st *record_state;
  struct tls_record_st record;

begin:

  if (empty_fragments > session->internals.priorities.max_empty_records)
    {
      gnutls_assert ();
      return GNUTLS_E_TOO_MANY_EMPTY_PACKETS;
    }

  if (session->internals.read_eof != 0)
    {
      /* if we have already read an EOF
       */
      return 0;
    }
  else if (session_is_valid (session) != 0
           || session->internals.may_not_read != 0)
    return gnutls_assert_val(GNUTLS_E_INVALID_SESSION);

  /* get the record state parameters */
  ret = _gnutls_epoch_get (session, EPOCH_READ_CURRENT, &record_params);
  if (ret < 0)
    return gnutls_assert_val (ret);

  /* Safeguard against processing data with an incomplete cipher state. */
  if (!record_params->initialized)
    return gnutls_assert_val (GNUTLS_E_INTERNAL_ERROR);

  record_state = &record_params->read;

  /* receive headers */
  ret = recv_headers(session, type, htype, &record, &ms);
  if (ret < 0)
    {
      ret = gnutls_assert_val_fatal(ret);
      goto recv_error;
    }

  if (IS_DTLS(session)) 
    packet_sequence = &record.sequence;
  else
    packet_sequence = &record_state->sequence_number;

  /* Read the packet data and insert it to record_recv_buffer.
   */
  ret =
       _gnutls_io_read_buffered (session, record.packet_size,
                                 record.type, &ms);
  if (ret != record.packet_size)
    {
      gnutls_assert();
      goto recv_error;
    }

  /* ok now we are sure that we have read all the data - so
   * move on !
   */
  ret = _mbuffer_linearize (&session->internals.record_recv_buffer);
  if (ret < 0)
    return gnutls_assert_val(ret);

  bufel = _mbuffer_head_get_first (&session->internals.record_recv_buffer, NULL);
  if (bufel == NULL)
    return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);

  /* We allocate the maximum possible to allow few compressed bytes to expand to a
   * full record.
   */
  decrypted = _mbuffer_alloc(record.length, record.length);
  if (decrypted == NULL)
    return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);

  ciphertext.data = (uint8_t*)_mbuffer_get_udata_ptr(bufel) + record.header_size;
  ciphertext.size = record.length;

  /* decrypt the data we got. 
   */
  t.data = _mbuffer_get_udata_ptr(decrypted);
  t.size = _mbuffer_get_udata_size(decrypted);
  ret =
    _gnutls_decrypt (session, &ciphertext, &t,
		     record.type, record_params, packet_sequence);
  if (ret >= 0) _mbuffer_set_udata_size(decrypted, ret);

  _mbuffer_head_remove_bytes (&session->internals.record_recv_buffer,
                              record.header_size + record.length);
  if (ret < 0)
    {
      gnutls_assert();
      _gnutls_audit_log(session, "Discarded message[%u] due to invalid decryption\n", 
            (unsigned int)_gnutls_uint64touint32 (packet_sequence));
      goto sanity_check_error;
    }

  /* check for duplicates. We check after the message
   * is processed and authenticated to avoid someone
   * messing with our windows.
   */
  if (IS_DTLS(session) && session->internals.no_replay_protection == 0)
    {
      ret = _dtls_record_check(record_params, packet_sequence);
      if (ret < 0)
        {
          _gnutls_audit_log(session, "Discarded duplicate message[%u.%u]: %s\n",
            (unsigned int)record.sequence.i[0]*256 +(unsigned int)record.sequence.i[1],
            (unsigned int) _gnutls_uint64touint32 (packet_sequence), _gnutls_packet2str (record.type));
          goto sanity_check_error;
        }
      _gnutls_record_log
        ("REC[%p]: Decrypted Packet[%u.%u] %s(%d) with length: %d\n", session,
        (unsigned int)record.sequence.i[0]*256 +(unsigned int)record.sequence.i[1],
        (unsigned int) _gnutls_uint64touint32 (packet_sequence),
        _gnutls_packet2str (record.type), record.type, (int)_mbuffer_get_udata_size(decrypted));
    }
  else
    {
      _gnutls_record_log
        ("REC[%p]: Decrypted Packet[%u] %s(%d) with length: %d\n", session,
        (unsigned int) _gnutls_uint64touint32 (packet_sequence),
        _gnutls_packet2str (record.type), record.type, (int)_mbuffer_get_udata_size(decrypted));
    }

  /* increase sequence number 
   */
  if (!IS_DTLS(session) && sequence_increment (session, &record_state->sequence_number) != 0)
    {
      session_invalidate (session);
      gnutls_assert ();
      ret = GNUTLS_E_RECORD_LIMIT_REACHED;
      goto sanity_check_error;
    }

/* (originally for) TLS 1.0 CBC protection. 
 * Actually this code is called if we just received
 * an empty packet. An empty TLS packet is usually
 * sent to protect some vulnerabilities in the CBC mode.
 * In that case we go to the beginning and start reading
 * the next packet.
 */
  if (_mbuffer_get_udata_size(decrypted) == 0)
    {
      _mbuffer_xfree(&decrypted);
      empty_fragments++;
      goto begin;
    }

  if (record.v2)
    decrypted->htype = GNUTLS_HANDSHAKE_CLIENT_HELLO_V2;
  else
    {
      uint8_t * p = _mbuffer_get_udata_ptr(decrypted);
      decrypted->htype = p[0];
    }

  ret =
    record_add_to_buffers (session, &record, type, htype, 
                           packet_sequence, decrypted);

  /* bufel is now either deinitialized or buffered somewhere else */

  if (ret < 0)
    return gnutls_assert_val(ret);

  return ret;

discard:
  session->internals.dtls.packets_dropped++;

  /* discard the whole received fragment. */
  bufel = _mbuffer_head_pop_first(&session->internals.record_recv_buffer);
  _mbuffer_xfree(&bufel);
  return gnutls_assert_val(GNUTLS_E_AGAIN);

sanity_check_error:
  if (IS_DTLS(session))
    {
      session->internals.dtls.packets_dropped++;
      ret = gnutls_assert_val(GNUTLS_E_AGAIN);
      goto cleanup;
    }

  session_unresumable (session);
  session_invalidate (session);

cleanup:
  _mbuffer_xfree(&decrypted);
  return ret;

recv_error:
  if (ret < 0 && (gnutls_error_is_fatal (ret) == 0 || ret == GNUTLS_E_TIMEDOUT))
    return ret;

  if (type == GNUTLS_ALERT) /* we were expecting close notify */
    {
      session_invalidate (session);
      gnutls_assert ();
      return 0;             
    }

  if (IS_DTLS(session))
    {
      goto discard;
    }

  session_invalidate (session);
  session_unresumable (session);

  if (ret == 0)
    return GNUTLS_E_UNEXPECTED_PACKET_LENGTH;
  else
    return ret;
}
Exemple #17
0
/* This function behaves exactly like write(). The only difference is
 * that it accepts, the gnutls_session_t and the content_type_t of data to
 * send (if called by the user the Content is specific)
 * It is intended to transfer data, under the current session.    
 *
 * @type: The content type to send
 * @htype: If this is a handshake message then the handshake type
 * @epoch_rel: %EPOCH_READ_* or %EPOCH_WRITE_*
 * @data: the data to be sent
 * @data_size: the size of the @data
 * @target_length: @data_size + minimum required padding
 * @mflags: zero or %MBUFFER_FLUSH
 *
 * Oct 30 2001: Removed capability to send data more than MAX_RECORD_SIZE.
 * This makes the function much easier to read, and more error resistant
 * (there were cases were the old function could mess everything up).
 * --nmav
 *
 * This function may accept a NULL pointer for data, and 0 for size, if
 * and only if the previous send was interrupted for some reason.
 *
 */
ssize_t
_gnutls_send_tlen_int (gnutls_session_t session, content_type_t type,
		gnutls_handshake_description_t htype,
		unsigned int epoch_rel, const void *_data,
		size_t data_size, size_t target_length, unsigned int mflags)
{
  mbuffer_st *bufel;
  ssize_t cipher_size;
  int retval, ret;
  int send_data_size;
  uint8_t *headers;
  int header_size;
  const uint8_t *data = _data;
  record_parameters_st *record_params;
  record_state_st *record_state;

  ret = _gnutls_epoch_get (session, epoch_rel, &record_params);
  if (ret < 0)
    return gnutls_assert_val(ret);

  /* Safeguard against processing data with an incomplete cipher state. */
  if (!record_params->initialized)
    return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);

  record_state = &record_params->write;

  /* Do not allow null pointer if the send buffer is empty.
   * If the previous send was interrupted then a null pointer is
   * ok, and means to resume.
   */
  if (session->internals.record_send_buffer.byte_length == 0 &&
      (data_size == 0 && _data == NULL))
    {
      gnutls_assert ();
      return GNUTLS_E_INVALID_REQUEST;
    }

  if (type != GNUTLS_ALERT)     /* alert messages are sent anyway */
    if (session_is_valid (session) || session->internals.may_not_write != 0)
      {
        gnutls_assert ();
        return GNUTLS_E_INVALID_SESSION;
      }


  if (data_size > MAX_USER_SEND_SIZE(session))
    {
      if (IS_DTLS(session))
        return gnutls_assert_val(GNUTLS_E_LARGE_PACKET);

      send_data_size = MAX_USER_SEND_SIZE(session);
    }
  else
    send_data_size = data_size;
  
  /* Only encrypt if we don't have data to send 
   * from the previous run. - probably interrupted.
   */
  if (mflags != 0 && session->internals.record_send_buffer.byte_length > 0)
    {
      ret = _gnutls_io_write_flush (session);
      if (ret > 0)
        cipher_size = ret;
      else
        cipher_size = 0;

      retval = session->internals.record_send_buffer_user_size;
    }
  else
    {
      if (unlikely((send_data_size == 0 && target_length == 0)))
        return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);

      /* now proceed to packet encryption
       */
      cipher_size = MAX_RECORD_SEND_SIZE(session);
      bufel = _mbuffer_alloc (0, cipher_size+CIPHER_SLACK_SIZE);
      if (bufel == NULL)
        return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);

      headers = _mbuffer_get_uhead_ptr(bufel);
      headers[0] = type;
      /* Use the default record version, if it is
       * set. */
      copy_record_version (session, htype, &headers[1]);
      header_size = RECORD_HEADER_SIZE(session);
      /* Adjust header length and add sequence for DTLS */
      if (IS_DTLS(session))
        memcpy(&headers[3], &record_state->sequence_number.i, 8);

      _gnutls_record_log
        ("REC[%p]: Preparing Packet %s(%d) with length: %d and target length: %d\n", session,
         _gnutls_packet2str (type), type, (int) data_size, (int) target_length);

      _mbuffer_set_udata_size(bufel, cipher_size);
      _mbuffer_set_uhead_size(bufel, header_size);

      ret =
        _gnutls_encrypt (session, 
                         data, send_data_size, target_length, 
                         bufel, type, record_params);
      if (ret <= 0)
        {
          gnutls_assert ();
          if (ret == 0)
            ret = GNUTLS_E_ENCRYPTION_FAILED;
          gnutls_free (bufel);
          return ret;   /* error */
        }

      cipher_size = _mbuffer_get_udata_size(bufel);
      retval = send_data_size;
      session->internals.record_send_buffer_user_size = send_data_size;

      /* increase sequence number
       */
      if (sequence_increment (session, &record_state->sequence_number) != 0)
        {
          session_invalidate (session);
          gnutls_free (bufel);
          return gnutls_assert_val(GNUTLS_E_RECORD_LIMIT_REACHED);
        }

      ret = _gnutls_io_write_buffered (session, bufel, mflags);
    }

  if (ret != cipher_size)
    {
      /* If we have sent any data then just return
       * the error value. Do not invalidate the session.
       */
      if (ret < 0 && gnutls_error_is_fatal (ret) == 0)
        return gnutls_assert_val(ret);

      if (ret > 0)
        ret = gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);

      session_unresumable (session);
      session->internals.may_not_write = 1;
      return gnutls_assert_val(ret);
    }

  session->internals.record_send_buffer_user_size = 0;

  _gnutls_record_log ("REC[%p]: Sent Packet[%d] %s(%d) in epoch %d and length: %d\n",
                      session,
                      (unsigned int)
                      _gnutls_uint64touint32
                      (&record_state->sequence_number),
                      _gnutls_packet2str (type), type, 
                      (int) record_params->epoch,
                      (int) cipher_size);

  return retval;
}