예제 #1
0
void
_gnutls_epoch_gc (gnutls_session_t session)
{
  int i, j;
  unsigned int min_index = 0;

  _gnutls_record_log ("REC[%p]: Start of epoch cleanup\n", session);

  /* Free all dead cipher state */
  for (i = 0; i < MAX_EPOCH_INDEX; i++)
    if (session->record_parameters[i] != NULL
        && !epoch_alive (session, session->record_parameters[i]))
      {
        _gnutls_epoch_free (session, session->record_parameters[i]);
        session->record_parameters[i] = NULL;
      }

  /* Look for contiguous NULLs at the start of the array */
  for (i = 0; i < MAX_EPOCH_INDEX && session->record_parameters[i] == NULL;
       i++);
  min_index = i;

  /* Pick up the slack in the epoch window. */
  for (i = 0, j = min_index; j < MAX_EPOCH_INDEX; i++, j++)
    session->record_parameters[i] = session->record_parameters[j];

  /* Set the new epoch_min */
  if (session->record_parameters[0] != NULL)
    session->security_parameters.epoch_min =
      session->record_parameters[0]->epoch;

  _gnutls_record_log ("REC[%p]: End of epoch cleanup\n", session);
}
예제 #2
0
/* Here we check if the advertized version is the one we
 * negotiated in the handshake.
 */
inline static int
record_check_version (gnutls_session_t session,
                      gnutls_handshake_description_t htype, uint8_t version[2])
{
  if (htype == GNUTLS_HANDSHAKE_CLIENT_HELLO)
    {
      /* Reject hello packets with major version higher than 3.
       */
      if (!(IS_DTLS(session)) && version[0] > 3)
        {
          gnutls_assert ();
          _gnutls_record_log
            ("REC[%p]: INVALID VERSION PACKET: (%d) %d.%d\n", session,
             htype, version[0], version[1]);
          return GNUTLS_E_UNSUPPORTED_VERSION_PACKET;
        }
    }
  else if (htype != GNUTLS_HANDSHAKE_SERVER_HELLO &&
           gnutls_protocol_get_version (session) !=
           _gnutls_version_get (version[0], version[1]))
    {
      /* Reject record packets that have a different version than the
       * one negotiated. Note that this version is not protected by any
       * mac. I don't really think that this check serves any purpose.
       */
      gnutls_assert ();
      _gnutls_record_log ("REC[%p]: INVALID VERSION PACKET: (%d) %d.%d\n",
                          session, htype, version[0], version[1]);

      return GNUTLS_E_UNSUPPORTED_VERSION_PACKET;
    }

  return 0;
}
예제 #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;
}
예제 #4
0
int
_gnutls_epoch_alloc (gnutls_session_t session, uint16_t epoch,
                     record_parameters_st ** out)
{
  record_parameters_st **slot;

  _gnutls_record_log ("REC[%p]: Allocating epoch #%u\n", session, epoch);

  slot = epoch_get_slot (session, epoch);

  /* If slot out of range or not empty. */
  if (slot == NULL)
    return gnutls_assert_val (GNUTLS_E_INVALID_REQUEST);

  if (*slot != NULL)
    return gnutls_assert_val (GNUTLS_E_INVALID_REQUEST);

  *slot = gnutls_calloc (1, sizeof (record_parameters_st));
  if (*slot == NULL)
    return gnutls_assert_val (GNUTLS_E_MEMORY_ERROR);

  (*slot)->epoch = epoch;
  (*slot)->cipher_algorithm = GNUTLS_CIPHER_UNKNOWN;
  (*slot)->mac_algorithm = GNUTLS_MAC_UNKNOWN;
  (*slot)->compression_algorithm = GNUTLS_COMP_UNKNOWN;

  if (IS_DTLS (session))
    _gnutls_write_uint16 (epoch, UINT64DATA((*slot)->write.sequence_number));

  if (out != NULL)
    *out = *slot;

  return 0;
}
예제 #5
0
파일: alert.c 프로젝트: GostCrypt/GnuTLS
/**
 * gnutls_alert_send:
 * @session: is a #gnutls_session_t type.
 * @level: is the level of the alert
 * @desc: is the alert description
 *
 * This function will send an alert to the peer in order to inform
 * him of something important (eg. his Certificate could not be verified).
 * If the alert level is Fatal then the peer is expected to close the
 * connection, otherwise he may ignore the alert and continue.
 *
 * The error code of the underlying record send function will be
 * returned, so you may also receive %GNUTLS_E_INTERRUPTED or
 * %GNUTLS_E_AGAIN as well.
 *
 * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise
 *   an error code is returned.
 **/
int
gnutls_alert_send(gnutls_session_t session, gnutls_alert_level_t level,
		  gnutls_alert_description_t desc)
{
	uint8_t data[2];
	int ret;
	const char *name;

	data[0] = (uint8_t) level;
	data[1] = (uint8_t) desc;

	name = gnutls_alert_get_name((int) data[1]);
	if (name == NULL)
		name = "(unknown)";
	_gnutls_record_log("REC: Sending Alert[%d|%d] - %s\n", data[0],
			   data[1], name);

	if ((ret =
	     _gnutls_send_int(session, GNUTLS_ALERT, -1,
			      EPOCH_WRITE_CURRENT, data, 2,
			      MBUFFER_FLUSH)) >= 0)
		return 0;
	else
		return ret;
}
예제 #6
0
void
_gnutls_epoch_free (gnutls_session_t session, record_parameters_st * params)
{
  _gnutls_record_log ("REC[%p]: Epoch #%u freed\n", session, params->epoch);

  free_record_state (&params->read, 1);
  free_record_state (&params->write, 0);

  gnutls_free (params);
}
예제 #7
0
파일: gnutls_record.c 프로젝트: ares89/vlc
/* Checks the record headers and returns the length, version and
 * content type.
 */
static int
record_check_headers (gnutls_session_t session,
                      uint8_t headers[RECORD_HEADER_SIZE],
                      content_type_t type,
                      gnutls_handshake_description_t htype,
                      /*output */ content_type_t * recv_type,
                      opaque version[2], uint16_t * length,
                      uint16_t * header_size)
{

  /* Read the first two bytes to determine if this is a 
   * version 2 message 
   */

  if (htype == GNUTLS_HANDSHAKE_CLIENT_HELLO && type == GNUTLS_HANDSHAKE
      && headers[0] > 127)
    {

      /* if msb set and expecting handshake message
       * it should be SSL 2 hello 
       */
      version[0] = 3;           /* assume SSL 3.0 */
      version[1] = 0;

      *length = (((headers[0] & 0x7f) << 8)) | headers[1];

      /* SSL 2.0 headers */
      *header_size = 2;
      *recv_type = GNUTLS_HANDSHAKE;    /* we accept only v2 client hello
                                         */

      /* in order to assist the handshake protocol.
       * V2 compatibility is a mess.
       */
      session->internals.v2_hello = *length;

      _gnutls_record_log ("REC[%p]: V2 packet received. Length: %d\n",
                          session, *length);

    }
  else
    {
      /* version 3.x 
       */
      *recv_type = headers[0];
      version[0] = headers[1];
      version[1] = headers[2];

      /* No DECR_LEN, since headers has enough size. 
       */
      *length = _gnutls_read_uint16 (&headers[3]);
    }

  return 0;
}
예제 #8
0
/* Checks the record headers and returns the length, version and
 * content type.
 */
static void
record_read_headers (gnutls_session_t session,
                      uint8_t headers[MAX_RECORD_HEADER_SIZE],
                      content_type_t type,
                      gnutls_handshake_description_t htype,
                      struct tls_record_st* record)
{

  /* Read the first two bytes to determine if this is a 
   * version 2 message 
   */

  if (htype == GNUTLS_HANDSHAKE_CLIENT_HELLO && type == GNUTLS_HANDSHAKE
      && headers[0] > 127 && !(IS_DTLS(session)))
    {

      /* if msb set and expecting handshake message
       * it should be SSL 2 hello 
       */
      record->version[0] = 3;           /* assume SSL 3.0 */
      record->version[1] = 0;

      record->length = (((headers[0] & 0x7f) << 8)) | headers[1];

      /* SSL 2.0 headers */
      record->header_size = record->packet_size = 2;
      record->type = GNUTLS_HANDSHAKE;    /* we accept only v2 client hello
                                         */

      /* in order to assist the handshake protocol.
       * V2 compatibility is a mess.
       */
      record->v2 = 1;
      record->epoch = 0;
      memset(&record->sequence, 0, sizeof(record->sequence));

      _gnutls_record_log ("REC[%p]: SSL 2.0 %s packet received. Length: %d\n",
                          session, 
                          _gnutls_packet2str (record->type),
                          record->length);

    }
  else 
    {
      /* dtls version 1.0 and TLS version 1.x */
      record->v2 = 0;

      record->type = headers[0];
      record->version[0] = headers[1];
      record->version[1] = headers[2];
      
      if(IS_DTLS(session))
        {
          memcpy(record->sequence.i, &headers[3], 8);
          record->length = _gnutls_read_uint16 (&headers[11]);
          record->epoch = _gnutls_read_uint16(record->sequence.i);
        }
      else
        {
	  memset(&record->sequence, 0, sizeof(record->sequence));
          record->length = _gnutls_read_uint16 (&headers[3]);
          record->epoch = 0;
        }

      _gnutls_record_log ("REC[%p]: SSL %d.%d %s packet received. Epoch %d, length: %d\n",
                          session, (int)record->version[0], (int)record->version[1], 
                          _gnutls_packet2str (record->type),
                          (int)record->epoch, record->length);

    }

  record->packet_size += record->length;
}
예제 #9
0
/* This function will check if the received record type is
 * the one we actually expect and adds it to the proper
 * buffer. The bufel will be deinitialized after calling
 * this function, even if it fails.
 */
static int
record_add_to_buffers (gnutls_session_t session,
                   struct tls_record_st *recv, content_type_t type,
                   gnutls_handshake_description_t htype, 
                   uint64* seq,
                   mbuffer_st* bufel)
{

  int ret;

  if ((recv->type == type)
      && (type == GNUTLS_APPLICATION_DATA ||
          type == GNUTLS_CHANGE_CIPHER_SPEC ||
          type == GNUTLS_HANDSHAKE))
    {
      _gnutls_record_buffer_put (session, type, seq, bufel);
      
      /* if we received application data as expected then we
       * deactivate the async timer */
      _dtls_async_timer_delete(session);
    }
  else
    {
      /* if the expected type is different than the received 
       */
      switch (recv->type)
        {
        case GNUTLS_ALERT:
          if (bufel->msg.size < 2)
            {
              ret = gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET_LENGTH);
              goto unexpected_packet;
            }

          _gnutls_record_log
            ("REC[%p]: Alert[%d|%d] - %s - was received\n", session,
             bufel->msg.data[0], bufel->msg.data[1], gnutls_alert_get_name ((int) bufel->msg.data[1]));

          session->internals.last_alert = bufel->msg.data[1];

          /* if close notify is received and
           * the alert is not fatal
           */
          if (bufel->msg.data[1] == GNUTLS_A_CLOSE_NOTIFY && bufel->msg.data[0] != GNUTLS_AL_FATAL)
            {
              /* If we have been expecting for an alert do 
               */
              session->internals.read_eof = 1;
              ret = GNUTLS_E_SESSION_EOF;
              goto cleanup;
            }
          else
            {
              /* if the alert is FATAL or WARNING
               * return the apropriate message
               */

              gnutls_assert ();
              ret = GNUTLS_E_WARNING_ALERT_RECEIVED;
              if (bufel->msg.data[0] == GNUTLS_AL_FATAL)
                {
                  session_unresumable (session);
                  session_invalidate (session);
                  ret = gnutls_assert_val(GNUTLS_E_FATAL_ALERT_RECEIVED);
                }
              goto cleanup;
            }
          break;

        case GNUTLS_CHANGE_CIPHER_SPEC:
          if (!(IS_DTLS(session)))
            return gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET);
            
          _gnutls_record_buffer_put (session, recv->type, seq, bufel);

          break;

#ifdef ENABLE_HEARTBEAT
        case GNUTLS_HEARTBEAT:
	    ret = _gnutls_heartbeat_handle (session, bufel);
	    goto cleanup;
#endif

        case GNUTLS_APPLICATION_DATA:
          if (session->internals.initial_negotiation_completed == 0)
            {
              ret = gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET);
              goto unexpected_packet;
            }


          /* the got_application data is only returned
           * if expecting client hello (for rehandshake
           * reasons). Otherwise it is an unexpected packet
           */
          if (type == GNUTLS_ALERT || (htype == GNUTLS_HANDSHAKE_CLIENT_HELLO
                                       && type == GNUTLS_HANDSHAKE))
            {
              /* even if data is unexpected put it into the buffer */
              if ((ret =
                   _gnutls_record_buffer_put (session, recv->type, seq,
                                              bufel)) < 0)
                {
                  gnutls_assert ();
                  goto cleanup;
                }

              return gnutls_assert_val(GNUTLS_E_GOT_APPLICATION_DATA);
            }
          else
            {
              ret = gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET);
              goto unexpected_packet;
            }

          break;

        case GNUTLS_HANDSHAKE:
          /* In DTLS we might receive a handshake replay from the peer to indicate
           * the our last TLS handshake messages were not received.
           */
          if (IS_DTLS(session))
            {
              if (type == GNUTLS_CHANGE_CIPHER_SPEC)
                {
                  ret = gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET);
                  goto unexpected_packet;
                }

              if (_dtls_is_async(session) && _dtls_async_timer_active(session))
                {
                  if (session->security_parameters.entity == GNUTLS_SERVER &&
                      bufel->htype == GNUTLS_HANDSHAKE_CLIENT_HELLO)
                    {
                      /* client requested rehandshake. Delete the timer */
                      _dtls_async_timer_delete(session);
                    }
                  else
                    {
                      session->internals.recv_state = RECV_STATE_DTLS_RETRANSMIT;
                      ret = _dtls_retransmit(session);
                      if (ret == 0) 
                        {
                          session->internals.recv_state = RECV_STATE_0;
                          ret = gnutls_assert_val(GNUTLS_E_AGAIN);
                          goto unexpected_packet;
                        }
                      goto cleanup;
                    }
                }
            }

          /* This is legal if HELLO_REQUEST is received - and we are a client.
           * If we are a server, a client may initiate a renegotiation at any time.
           */
          if (session->security_parameters.entity == GNUTLS_SERVER &&
              bufel->htype == GNUTLS_HANDSHAKE_CLIENT_HELLO)
            {
              gnutls_assert ();
              ret =
                _gnutls_record_buffer_put (session, recv->type, seq, bufel);
              if (ret < 0)
                {
                  gnutls_assert ();
                  goto cleanup;
                }
              return GNUTLS_E_REHANDSHAKE;
            }

          /* If we are already in a handshake then a Hello
           * Request is illegal. But here we don't really care
           * since this message will never make it up here.
           */

          /* So we accept it, if it is a Hello. If not, this will
           * fail and trigger flight retransmissions after some time. */
          ret = _gnutls_recv_hello_request (session, bufel->msg.data, bufel->msg.size);
          goto unexpected_packet;

          break;
        default:

          _gnutls_record_log
            ("REC[%p]: Received unexpected packet %d (%s) expecting %d (%s)\n",
             session, recv->type, _gnutls_packet2str(recv->type), type, _gnutls_packet2str(type));

          gnutls_assert ();
          ret = GNUTLS_E_UNEXPECTED_PACKET;
          goto unexpected_packet;
        }
    }

  return 0;

unexpected_packet:
  if (IS_DTLS(session) && ret != GNUTLS_E_REHANDSHAKE)
    {
      _mbuffer_xfree(&bufel);
      RETURN_DTLS_EAGAIN_OR_TIMEOUT(session, ret);
    }

cleanup:
  _mbuffer_xfree(&bufel);
  return ret;

}
예제 #10
0
/* Here we check if the advertized version is the one we
 * negotiated in the handshake.
 */
inline static int
record_check_version (gnutls_session_t session,
                      gnutls_handshake_description_t htype, uint8_t version[2])
{
const version_entry_st* vers = get_version (session);
int diff = 0;

  if (vers->major != version[0] || vers->minor != version[1])
    diff = 1;

  if (!IS_DTLS(session))
    {
      if (htype == GNUTLS_HANDSHAKE_CLIENT_HELLO ||
          htype == GNUTLS_HANDSHAKE_SERVER_HELLO)
        {
          if (version[0] != 3)
            {
              gnutls_assert ();
              _gnutls_record_log
                ("REC[%p]: INVALID VERSION PACKET: (%d) %d.%d\n", session,
                 htype, version[0], version[1]);
              return GNUTLS_E_UNSUPPORTED_VERSION_PACKET;
            }
        }
      else if (diff != 0)
        {
          /* Reject record packets that have a different version than the
           * one negotiated. Note that this version is not protected by any
           * mac. I don't really think that this check serves any purpose.
           */
          gnutls_assert ();
          _gnutls_record_log ("REC[%p]: INVALID VERSION PACKET: (%d) %d.%d\n",
                          session, htype, version[0], version[1]);

          return GNUTLS_E_UNSUPPORTED_VERSION_PACKET;
        }
    }
  else /* DTLS */
    {
      /* In DTLS the only information we have here is whether we
       * expect a handshake message or not.
       */
      if (htype == (gnutls_handshake_description_t)-1)
        {
          if (diff)
            {
              /* Reject record packets that have a different version than the
               * one negotiated. Note that this version is not protected by any
               * mac. I don't really think that this check serves any purpose.
               */
              gnutls_assert ();
              _gnutls_record_log ("REC[%p]: INVALID VERSION PACKET: (%d) %d.%d\n",
                              session, htype, version[0], version[1]);

              return GNUTLS_E_UNSUPPORTED_VERSION_PACKET;
            }
        }
      else if (vers->id > GNUTLS_DTLS1_0 && version[0] > 254)
        {
          gnutls_assert ();
          _gnutls_record_log("REC[%p]: INVALID DTLS VERSION PACKET: (%d) %d.%d\n", session,
                 htype, version[0], version[1]);
          return GNUTLS_E_UNSUPPORTED_VERSION_PACKET;
        }
      else if (vers->id == GNUTLS_DTLS0_9 && version[0] > 1)
        {
          gnutls_assert ();
          _gnutls_record_log("REC[%p]: INVALID DTLS VERSION PACKET: (%d) %d.%d\n", session,
                 htype, version[0], version[1]);
          return GNUTLS_E_UNSUPPORTED_VERSION_PACKET;
        }
    }

  return 0;
}
예제 #11
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;
}
예제 #12
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;
}
예제 #13
0
static int recv_headers( gnutls_session_t session, content_type_t type, 
                         gnutls_handshake_description_t htype, 
                         struct tls_record_st* record,
                         unsigned int *ms)
{
int ret;
gnutls_datum_t raw; /* raw headers */
  /* Read the headers.
   */
  record->header_size = record->packet_size = RECORD_HEADER_SIZE(session);

  ret =
       _gnutls_io_read_buffered (session, record->header_size, -1, ms);
  if (ret != record->header_size)
    {
      if (ret < 0 && gnutls_error_is_fatal (ret) == 0)
        return ret;
      
      if (ret > 0)
        ret = GNUTLS_E_UNEXPECTED_PACKET_LENGTH;
      else if (ret == 0)
        ret = GNUTLS_E_PREMATURE_TERMINATION;

      return gnutls_assert_val(ret);
    }

  ret = _mbuffer_linearize (&session->internals.record_recv_buffer);
  if (ret < 0)
    return gnutls_assert_val(ret);

  _mbuffer_head_get_first (&session->internals.record_recv_buffer, &raw);
  if (raw.size < RECORD_HEADER_SIZE(session))
    return gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET_LENGTH);

  record_read_headers (session, raw.data, type, htype, record);

  /* Check if the DTLS epoch is valid */
  if (IS_DTLS(session)) 
    {
      if (_gnutls_epoch_is_valid(session, record->epoch) == 0)
        {
          _gnutls_audit_log(session, "Discarded message[%u] with invalid epoch %u.\n",
            (unsigned int)_gnutls_uint64touint32 (&record->sequence), 
            (unsigned int)record->sequence.i[0]*256+(unsigned int)record->sequence.i[1]);
          gnutls_assert();
          /* doesn't matter, just a fatal error */
          return GNUTLS_E_UNEXPECTED_PACKET_LENGTH;
        }
    }

  /* Here we check if the Type of the received packet is
   * ok. 
   */
  if ((ret = check_recv_type (session, record->type)) < 0)
    return gnutls_assert_val(ret);

  /* Here we check if the advertized version is the one we
   * negotiated in the handshake.
   */
  if ((ret = record_check_version (session, htype, record->version)) < 0)
    return gnutls_assert_val(ret);

  if (record->length > MAX_RECV_SIZE(session))
    {
      _gnutls_audit_log
        (session, "Received packet with illegal length: %u\n", (unsigned int)record->length);
      return gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET_LENGTH);
    }

  _gnutls_record_log
    ("REC[%p]: Expected Packet %s(%d)\n", session,
     _gnutls_packet2str (type), type);
  _gnutls_record_log ("REC[%p]: Received Packet %s(%d) with length: %d\n",
                      session,
                      _gnutls_packet2str (record->type), record->type, record->length);

  
  return 0;
}
예제 #14
0
파일: gnutls_record.c 프로젝트: ares89/vlc
/* 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;
}
예제 #15
0
파일: gnutls_record.c 프로젝트: ares89/vlc
/* This function will check if the received record type is
 * the one we actually expect.
 */
static int
record_check_type (gnutls_session_t session,
                   content_type_t recv_type, content_type_t type,
                   gnutls_handshake_description_t htype, opaque * data,
                   int data_size)
{

  int ret;

  if ((recv_type == type)
      && (type == GNUTLS_APPLICATION_DATA ||
          type == GNUTLS_HANDSHAKE || type == GNUTLS_INNER_APPLICATION))
    {
      _gnutls_record_buffer_put (type, session, (void *) data, data_size);
    }
  else
    {
      switch (recv_type)
        {
        case GNUTLS_ALERT:

          _gnutls_record_log
            ("REC[%p]: Alert[%d|%d] - %s - was received\n", session,
             data[0], data[1], gnutls_alert_get_name ((int) data[1]));

          session->internals.last_alert = data[1];

          /* if close notify is received and
           * the alert is not fatal
           */
          if (data[1] == GNUTLS_A_CLOSE_NOTIFY && data[0] != GNUTLS_AL_FATAL)
            {
              /* If we have been expecting for an alert do 
               */
              session->internals.read_eof = 1;
              return GNUTLS_E_INT_RET_0;        /* EOF */
            }
          else
            {

              /* if the alert is FATAL or WARNING
               * return the apropriate message
               */

              gnutls_assert ();
              ret = GNUTLS_E_WARNING_ALERT_RECEIVED;
              if (data[0] == GNUTLS_AL_FATAL)
                {
                  session_unresumable (session);
                  session_invalidate (session);
                  ret = GNUTLS_E_FATAL_ALERT_RECEIVED;
                }

              return ret;
            }
          break;

        case GNUTLS_CHANGE_CIPHER_SPEC:
          /* this packet is now handled in the recv_int()
           * function
           */
          gnutls_assert ();

          return GNUTLS_E_UNEXPECTED_PACKET;

        case GNUTLS_APPLICATION_DATA:
          if (session->internals.initial_negotiation_completed == 0)
            {
              return gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET);
            }

          /* even if data is unexpected put it into the buffer */
          if ((ret =
               _gnutls_record_buffer_put (recv_type, session,
                                          (void *) data, data_size)) < 0)
            {
              gnutls_assert ();
              return ret;
            }

          /* the got_application data is only returned
           * if expecting client hello (for rehandshake
           * reasons). Otherwise it is an unexpected packet
           */
          if (type == GNUTLS_ALERT || (htype == GNUTLS_HANDSHAKE_CLIENT_HELLO
                                       && type == GNUTLS_HANDSHAKE))
            return GNUTLS_E_GOT_APPLICATION_DATA;
          else
            {
              gnutls_assert ();
              return GNUTLS_E_UNEXPECTED_PACKET;
            }

          break;
        case GNUTLS_HANDSHAKE:
          /* This is legal if HELLO_REQUEST is received - and we are a client.
           * If we are a server, a client may initiate a renegotiation at any time.
           */
          if (session->security_parameters.entity == GNUTLS_SERVER)
            {
              gnutls_assert ();
              ret =
                _gnutls_record_buffer_put (recv_type, session, (void *) data,
                                           data_size);
              if (ret < 0)
                {
                  gnutls_assert ();
                  return ret;
                }
              return GNUTLS_E_REHANDSHAKE;
            }

          /* If we are already in a handshake then a Hello
           * Request is illegal. But here we don't really care
           * since this message will never make it up here.
           */

          /* So we accept it */
          return _gnutls_recv_hello_request (session, data, data_size);

          break;
        case GNUTLS_INNER_APPLICATION:
          /* even if data is unexpected put it into the buffer */
          if ((ret = _gnutls_record_buffer_put (recv_type, session,
                                                (void *) data,
                                                data_size)) < 0)
            {
              gnutls_assert ();
              return ret;
            }
          gnutls_assert ();
          return GNUTLS_E_UNEXPECTED_PACKET;
          break;
        default:

          _gnutls_record_log
            ("REC[%p]: Received Unknown packet %d expecting %d\n",
             session, recv_type, type);

          gnutls_assert ();
          return GNUTLS_E_INTERNAL_ERROR;
        }
    }

  return 0;

}
예제 #16
0
파일: gnutls_record.c 프로젝트: ares89/vlc
/* 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;
}
예제 #17
0
/* Deciphers the ciphertext packet, and puts the result to compress_data, of compress_size.
 * Returns the actual compressed packet size.
 */
int
_gnutls_ciphertext2compressed (gnutls_session_t session,
			       opaque * compress_data,
			       int compress_size,
			       gnutls_datum_t ciphertext, uint8_t type)
{
  uint8_t MAC[MAX_HASH_SIZE];
  uint16_t c_length;
  uint8_t pad;
  int length;
  digest_hd_st td;
  uint16_t blocksize;
  int ret, i, pad_failed = 0;
  uint8_t major, minor;
  gnutls_protocol_t ver;
  int hash_size =
    _gnutls_hash_get_algo_len (session->security_parameters.
			       read_mac_algorithm);

  ver = gnutls_protocol_get_version (session);
  minor = _gnutls_version_get_minor (ver);
  major = _gnutls_version_get_major (ver);

  blocksize =
    _gnutls_cipher_get_block_size (session->security_parameters.
				   read_bulk_cipher_algorithm);

  /* initialize MAC 
   */
  ret = mac_init (&td, session->security_parameters.read_mac_algorithm,
		  session->connection_state.read_mac_secret.data,
		  session->connection_state.read_mac_secret.size, ver);

  if (ret < 0
      && session->security_parameters.read_mac_algorithm != GNUTLS_MAC_NULL)
    {
      gnutls_assert ();
      return GNUTLS_E_INTERNAL_ERROR;
    }

  /* actual decryption (inplace)
   */
  switch (_gnutls_cipher_is_block
	  (session->security_parameters.read_bulk_cipher_algorithm))
    {
    case CIPHER_STREAM:
      if ((ret =
	   _gnutls_cipher_decrypt (&session->connection_state.
				   read_cipher_state, ciphertext.data,
				   ciphertext.size)) < 0)
	{
	  gnutls_assert ();
	  return ret;
	}

      length = ciphertext.size - hash_size;

      break;
    case CIPHER_BLOCK:
      if ((ciphertext.size < blocksize) || (ciphertext.size % blocksize != 0))
	{
	  gnutls_assert ();
	  return GNUTLS_E_DECRYPTION_FAILED;
	}

      if ((ret =
	   _gnutls_cipher_decrypt (&session->connection_state.
				   read_cipher_state, ciphertext.data,
				   ciphertext.size)) < 0)
	{
	  gnutls_assert ();
	  return ret;
	}

      /* ignore the IV in TLS 1.1.
       */
      if (session->security_parameters.version >= GNUTLS_TLS1_1)
	{
	  ciphertext.size -= blocksize;
	  ciphertext.data += blocksize;

	  if (ciphertext.size == 0)
	    {
	      gnutls_assert ();
	      return GNUTLS_E_DECRYPTION_FAILED;
	    }
	}

      pad = ciphertext.data[ciphertext.size - 1] + 1;	/* pad */

      if ((int) pad > (int) ciphertext.size - hash_size)
	{
	  gnutls_assert ();
	  _gnutls_record_log
	    ("REC[%x]: Short record length %d > %d - %d (under attack?)\n",
	     session, pad, ciphertext.size, hash_size);
	  /* We do not fail here. We check below for the
	   * the pad_failed. If zero means success.
	   */
	  pad_failed = GNUTLS_E_DECRYPTION_FAILED;
	}

      length = ciphertext.size - hash_size - pad;

      /* Check the pading bytes (TLS 1.x)
       */
      if (ver >= GNUTLS_TLS1 && pad_failed == 0)
	for (i = 2; i < pad; i++)
	  {
	    if (ciphertext.data[ciphertext.size - i] !=
		ciphertext.data[ciphertext.size - 1])
	      pad_failed = GNUTLS_E_DECRYPTION_FAILED;
	  }
      break;
    default:
      gnutls_assert ();
      return GNUTLS_E_INTERNAL_ERROR;
    }

  if (length < 0)
    length = 0;
  c_length = _gnutls_conv_uint16 ((uint16_t) length);

  /* Pass the type, version, length and compressed through
   * MAC.
   */
  if (session->security_parameters.read_mac_algorithm != GNUTLS_MAC_NULL)
    {
      _gnutls_hmac (&td,
		    UINT64DATA (session->connection_state.
				read_sequence_number), 8);

      _gnutls_hmac (&td, &type, 1);
      if (ver >= GNUTLS_TLS1)
	{			/* TLS 1.x */
	  _gnutls_hmac (&td, &major, 1);
	  _gnutls_hmac (&td, &minor, 1);
	}
      _gnutls_hmac (&td, &c_length, 2);

      if (length > 0)
	_gnutls_hmac (&td, ciphertext.data, length);

      mac_deinit (&td, MAC, ver);
    }

  /* This one was introduced to avoid a timing attack against the TLS
   * 1.0 protocol.
   */
  if (pad_failed != 0)
    return pad_failed;

  /* HMAC was not the same. 
   */
  if (memcmp (MAC, &ciphertext.data[length], hash_size) != 0)
    {
      gnutls_assert ();
      return GNUTLS_E_DECRYPTION_FAILED;
    }

  /* copy the decrypted stuff to compress_data.
   */
  if (compress_size < length)
    {
      gnutls_assert ();
      return GNUTLS_E_DECOMPRESSION_FAILED;
    }
  memcpy (compress_data, ciphertext.data, length);

  return length;
}
예제 #18
0
/*
 * Processes a heartbeat message. 
 */
int _gnutls_heartbeat_handle(gnutls_session_t session, mbuffer_st * bufel)
{
	int ret;
	unsigned type;
	unsigned pos;
	uint8_t *msg = _mbuffer_get_udata_ptr(bufel);
	size_t hb_len, len = _mbuffer_get_udata_size(bufel);

	if (gnutls_heartbeat_allowed
	    (session, GNUTLS_HB_PEER_ALLOWED_TO_SEND) == 0)
		return gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET);

	if (len < 4)
		return
		    gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET_LENGTH);

	pos = 0;
	type = msg[pos++];

	hb_len = _gnutls_read_uint16(&msg[pos]);
	if (hb_len > len - 3)
		return
		    gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET_LENGTH);

	pos += 2;

	switch (type) {
	case HEARTBEAT_REQUEST:
		_gnutls_buffer_reset(&session->internals.hb_remote_data);

		ret =
		    _gnutls_buffer_resize(&session->internals.
					  hb_remote_data, hb_len);
		if (ret < 0)
			return gnutls_assert_val(ret);

		if (hb_len > 0)
			memcpy(session->internals.hb_remote_data.data,
			       &msg[pos], hb_len);
		session->internals.hb_remote_data.length = hb_len;

		return gnutls_assert_val(GNUTLS_E_HEARTBEAT_PING_RECEIVED);

	case HEARTBEAT_RESPONSE:

		if (hb_len != session->internals.hb_local_data.length)
			return
			    gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET);

		if (hb_len > 0 &&
		    memcmp(&msg[pos],
			   session->internals.hb_local_data.data,
			   hb_len) != 0) {
			if (IS_DTLS(session))
				return gnutls_assert_val(GNUTLS_E_AGAIN);	/* ignore it */
			else
				return
				    gnutls_assert_val
				    (GNUTLS_E_UNEXPECTED_PACKET);
		}

		_gnutls_buffer_reset(&session->internals.hb_local_data);

		return gnutls_assert_val(GNUTLS_E_HEARTBEAT_PONG_RECEIVED);
	default:
		_gnutls_record_log
		    ("REC[%p]: HB: received unknown type %u\n", session,
		     type);
		return gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET);
	}
}
예제 #19
0
/* Deciphers the ciphertext packet, and puts the result to compress_data, of compress_size.
 * Returns the actual compressed packet size.
 */
static int
ciphertext_to_compressed (gnutls_session_t session,
                          gnutls_datum_t *ciphertext, 
                          opaque * compress_data,
                          int compress_size,
                          uint8_t type, record_parameters_st * params, 
                          uint64* sequence)
{
  uint8_t tag[MAX_HASH_SIZE];
  uint8_t pad;
  int length, length_to_decrypt;
  uint16_t blocksize;
  int ret, i, pad_failed = 0;
  opaque preamble[MAX_PREAMBLE_SIZE];
  int preamble_size;
  int ver = gnutls_protocol_get_version (session);
  int tag_size = _gnutls_auth_cipher_tag_len (&params->read.cipher_state);
  int explicit_iv = _gnutls_version_has_explicit_iv (session->security_parameters.version);

  blocksize = gnutls_cipher_get_block_size (params->cipher_algorithm);

  /* actual decryption (inplace)
   */
  switch (_gnutls_cipher_is_block (params->cipher_algorithm))
    {
    case CIPHER_STREAM:
      /* The way AEAD ciphers are defined in RFC5246, it allows
       * only stream ciphers.
       */
      if (explicit_iv && _gnutls_auth_cipher_is_aead(&params->read.cipher_state))
        {
          uint8_t nonce[blocksize];
          /* Values in AEAD are pretty fixed in TLS 1.2 for 128-bit block
           */
          if (params->read.IV.data == NULL || params->read.IV.size != 4)
            return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
          
          if (ciphertext->size < tag_size+AEAD_EXPLICIT_DATA_SIZE)
            return gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET_LENGTH);

          memcpy(nonce, params->read.IV.data, AEAD_IMPLICIT_DATA_SIZE);
          memcpy(&nonce[AEAD_IMPLICIT_DATA_SIZE], ciphertext->data, AEAD_EXPLICIT_DATA_SIZE);
          
          _gnutls_auth_cipher_setiv(&params->read.cipher_state, nonce, AEAD_EXPLICIT_DATA_SIZE+AEAD_IMPLICIT_DATA_SIZE);

          ciphertext->data += AEAD_EXPLICIT_DATA_SIZE;
          ciphertext->size -= AEAD_EXPLICIT_DATA_SIZE;
          
          length_to_decrypt = ciphertext->size - tag_size;
        }
      else
        {
          if (ciphertext->size < tag_size)
            return gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET_LENGTH);
  
          length_to_decrypt = ciphertext->size;
        }

      length = ciphertext->size - tag_size;

      /* Pass the type, version, length and compressed through
       * MAC.
       */
      preamble_size =
        make_preamble (UINT64DATA(*sequence), type,
                       length, ver, preamble);

      _gnutls_auth_cipher_add_auth (&params->read.cipher_state, preamble, preamble_size);

      if ((ret =
           _gnutls_auth_cipher_decrypt (&params->read.cipher_state,
             ciphertext->data, length_to_decrypt)) < 0)
        return gnutls_assert_val(ret);

      break;
    case CIPHER_BLOCK:
      if (ciphertext->size < MAX(blocksize, tag_size) || (ciphertext->size % blocksize != 0))
        return gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET_LENGTH);

      /* ignore the IV in TLS 1.1+
       */
      if (explicit_iv)
        {
          _gnutls_auth_cipher_setiv(&params->read.cipher_state,
            ciphertext->data, blocksize);

          ciphertext->size -= blocksize;
          ciphertext->data += blocksize;

          if (ciphertext->size == 0)
            {
              gnutls_assert ();
              return GNUTLS_E_DECRYPTION_FAILED;
            }
        }

      /* we don't use the auth_cipher interface here, since
       * TLS with block ciphers is impossible to be used under such
       * an API. (the length of plaintext is required to calculate
       * auth_data, but it is not available before decryption).
       */
      if ((ret =
           _gnutls_cipher_decrypt (&params->read.cipher_state.cipher,
             ciphertext->data, ciphertext->size)) < 0)
        return gnutls_assert_val(ret);

      pad = ciphertext->data[ciphertext->size - 1] + 1;   /* pad */

      if ((int) pad > (int) ciphertext->size - tag_size)
        {
          gnutls_assert ();
          _gnutls_record_log
            ("REC[%p]: Short record length %d > %d - %d (under attack?)\n",
             session, pad, ciphertext->size, tag_size);
          /* We do not fail here. We check below for the
           * the pad_failed. If zero means success.
           */
          pad_failed = GNUTLS_E_DECRYPTION_FAILED;
          pad %= blocksize;
        }

      length = ciphertext->size - tag_size - pad;

      /* Check the pading bytes (TLS 1.x)
       */
      if (ver != GNUTLS_SSL3)
        for (i = 2; i < pad; i++)
          {
            if (ciphertext->data[ciphertext->size - i] !=
                ciphertext->data[ciphertext->size - 1])
              pad_failed = GNUTLS_E_DECRYPTION_FAILED;
          }

      if (length < 0)
        length = 0;

      /* Pass the type, version, length and compressed through
       * MAC.
       */
      preamble_size =
        make_preamble (UINT64DATA(*sequence), type,
                       length, ver, preamble);
      _gnutls_auth_cipher_add_auth (&params->read.cipher_state, preamble, preamble_size);
      _gnutls_auth_cipher_add_auth (&params->read.cipher_state, ciphertext->data, length);

      break;
    default:
      return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
    }

  ret = _gnutls_auth_cipher_tag(&params->read.cipher_state, tag, tag_size);
  if (ret < 0)
    return gnutls_assert_val(ret);

  /* This one was introduced to avoid a timing attack against the TLS
   * 1.0 protocol.
   */
  /* HMAC was not the same. 
   */
  if (memcmp (tag, &ciphertext->data[length], tag_size) != 0 || pad_failed != 0)
    return gnutls_assert_val(GNUTLS_E_DECRYPTION_FAILED);

  /* copy the decrypted stuff to compress_data.
   */
  if (compress_size < length)
    return gnutls_assert_val(GNUTLS_E_DECOMPRESSION_FAILED);

  if (compress_data != ciphertext->data)
    memcpy (compress_data, ciphertext->data, length);

  return length;
}
예제 #20
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;
}
예제 #21
0
/* Deciphers the ciphertext packet, and puts the result to compress_data, of compress_size.
 * Returns the actual compressed packet size.
 */
int
_gnutls_ciphertext2compressed (gnutls_session_t session,
                               opaque * compress_data,
                               int compress_size,
                               gnutls_datum_t ciphertext, uint8_t type,
                               record_parameters_st * params)
{
  uint8_t MAC[MAX_HASH_SIZE];
  uint16_t c_length;
  uint8_t pad;
  int length;
  uint16_t blocksize;
  int ret, i, pad_failed = 0;
  opaque preamble[PREAMBLE_SIZE];
  int preamble_size;
  int ver = gnutls_protocol_get_version (session);
  int hash_size = _gnutls_hash_get_algo_len (params->mac_algorithm);

  blocksize = gnutls_cipher_get_block_size (params->cipher_algorithm);


  /* actual decryption (inplace)
   */
  switch (_gnutls_cipher_is_block (params->cipher_algorithm))
    {
    case CIPHER_STREAM:
      if ((ret =
           _gnutls_cipher_decrypt (&params->read.cipher_state,
                                   ciphertext.data, ciphertext.size)) < 0)
        {
          gnutls_assert ();
          return ret;
        }

      length = ciphertext.size - hash_size;

      break;
    case CIPHER_BLOCK:
      if ((ciphertext.size < blocksize) || (ciphertext.size % blocksize != 0))
        {
          gnutls_assert ();
          return GNUTLS_E_DECRYPTION_FAILED;
        }

      if ((ret =
           _gnutls_cipher_decrypt (&params->read.cipher_state,
                                   ciphertext.data, ciphertext.size)) < 0)
        {
          gnutls_assert ();
          return ret;
        }

      /* ignore the IV in TLS 1.1.
       */
      if (_gnutls_version_has_explicit_iv
          (session->security_parameters.version))
        {
          ciphertext.size -= blocksize;
          ciphertext.data += blocksize;

          if (ciphertext.size == 0)
            {
              gnutls_assert ();
              return GNUTLS_E_DECRYPTION_FAILED;
            }
        }

      pad = ciphertext.data[ciphertext.size - 1] + 1;   /* pad */

      if ((int) pad > (int) ciphertext.size - hash_size)
        {
          gnutls_assert ();
          _gnutls_record_log
            ("REC[%p]: Short record length %d > %d - %d (under attack?)\n",
             session, pad, ciphertext.size, hash_size);
          /* We do not fail here. We check below for the
           * the pad_failed. If zero means success.
           */
          pad_failed = GNUTLS_E_DECRYPTION_FAILED;
        }

      length = ciphertext.size - hash_size - pad;

      /* Check the pading bytes (TLS 1.x)
       */
      if (_gnutls_version_has_variable_padding (ver) && pad_failed == 0)
        for (i = 2; i < pad; i++)
          {
            if (ciphertext.data[ciphertext.size - i] !=
                ciphertext.data[ciphertext.size - 1])
              pad_failed = GNUTLS_E_DECRYPTION_FAILED;
          }
      break;
    default:
      gnutls_assert ();
      return GNUTLS_E_INTERNAL_ERROR;
    }

  if (length < 0)
    length = 0;
  c_length = _gnutls_conv_uint16 ((uint16_t) length);

  /* Pass the type, version, length and compressed through
   * MAC.
   */
  if (params->mac_algorithm != GNUTLS_MAC_NULL)
    {
      digest_hd_st td;

      ret = mac_init (&td, params->mac_algorithm,
                      params->read.mac_secret.data,
                      params->read.mac_secret.size, ver);

      if (ret < 0)
        {
          gnutls_assert ();
          return GNUTLS_E_INTERNAL_ERROR;
        }

      preamble_size =
        make_preamble (UINT64DATA
                       (params->read.sequence_number), type,
                       c_length, ver, preamble);
      mac_hash (&td, preamble, preamble_size, ver);
      if (length > 0)
        mac_hash (&td, ciphertext.data, length, ver);

      mac_deinit (&td, MAC, ver);
    }

  /* This one was introduced to avoid a timing attack against the TLS
   * 1.0 protocol.
   */
  if (pad_failed != 0)
    {
      gnutls_assert ();
      return pad_failed;
    }

  /* HMAC was not the same. 
   */
  if (memcmp (MAC, &ciphertext.data[length], hash_size) != 0)
    {
      gnutls_assert ();
      return GNUTLS_E_DECRYPTION_FAILED;
    }

  /* copy the decrypted stuff to compress_data.
   */
  if (compress_size < length)
    {
      gnutls_assert ();
      return GNUTLS_E_DECOMPRESSION_FAILED;
    }
  memcpy (compress_data, ciphertext.data, length);

  return length;
}
예제 #22
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, const void *_data,
		  size_t sizeofdata)
{
  uint8_t *cipher;
  int cipher_size;
  int retval, ret;
  int data2send_size;
  uint8_t headers[5];
  const uint8_t *data = _data;
  int erecord_size = 0;
  opaque *erecord = NULL;

  /* 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.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[%x]: Sending Packet[%d] %s(%d) with length: %d\n", session,
     (int) _gnutls_uint64touint32 (&session->connection_state.
				   write_sequence_number),
     _gnutls_packet2str (type), type, 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 (session->internals.record_send_buffer.length > 0)
    {
      ret = _gnutls_io_write_flush (session);
      if (ret > 0)
	cipher_size = ret;
      else
	cipher_size = 0;

      cipher = NULL;

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

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

      cipher_size =
	_gnutls_encrypt (session, headers, RECORD_HEADER_SIZE, data,
			 data2send_size, cipher, cipher_size, type, 1);
      if (cipher_size <= 0)
	{
	  gnutls_assert ();
	  if (cipher_size == 0)
	    cipher_size = GNUTLS_E_ENCRYPTION_FAILED;
	  gnutls_afree (erecord);
	  gnutls_free (cipher);
	  return cipher_size;	/* error */
	}

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

      /* increase sequence number
       */
      if (_gnutls_uint64pp
	  (&session->connection_state.write_sequence_number) != 0)
	{
	  session_invalidate (session);
	  gnutls_assert ();
	  gnutls_afree (erecord);
	  gnutls_free (cipher);
	  return GNUTLS_E_RECORD_LIMIT_REACHED;
	}

      ret =
	_gnutls_io_write_buffered2 (session, erecord, erecord_size,
				    cipher, cipher_size);
      gnutls_afree (erecord);
      gnutls_free (cipher);
    }

  if (ret != cipher_size + erecord_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[%x]: Sent Packet[%d] %s(%d) with length: %d\n",
		      session,
		      (int) _gnutls_uint64touint32 (&session->
						    connection_state.
						    write_sequence_number),
		      _gnutls_packet2str (type), type, cipher_size);

  return retval;
}