示例#1
0
/* This is a receive function for the gnutls handshake
 * protocol. Makes sure that we have received all data.
 */
ssize_t
MHD_gtls_handshake_io_recv_int (MHD_gtls_session_t session,
                                content_type_t type,
                                MHD_gnutls_handshake_description_t htype,
                                void *iptr, size_t sizeOfPtr)
{
  size_t left;
  ssize_t i;
  opaque *ptr;
  size_t dsize;

  ptr = iptr;
  left = sizeOfPtr;

  if (sizeOfPtr == 0 || iptr == NULL)
    {
      MHD_gnutls_assert ();
      return GNUTLS_E_INVALID_REQUEST;
    }

  if (session->internals.handshake_recv_buffer.length > 0)
    {
      /* if we have already received some data */
      if (sizeOfPtr <= session->internals.handshake_recv_buffer.length)
        {
          /* if requested less data then return it.
           */
          MHD_gnutls_assert ();
          memcpy (iptr, session->internals.handshake_recv_buffer.data,
                  sizeOfPtr);

          session->internals.handshake_recv_buffer.length -= sizeOfPtr;

          memmove (session->internals.handshake_recv_buffer.data,
                   &session->internals.handshake_recv_buffer.data[sizeOfPtr],
                   session->internals.handshake_recv_buffer.length);

          return sizeOfPtr;
        }
      MHD_gnutls_assert ();
      memcpy (iptr, session->internals.handshake_recv_buffer.data,
              session->internals.handshake_recv_buffer.length);

      htype = session->internals.handshake_recv_buffer_htype;
      type = session->internals.handshake_recv_buffer_type;

      left -= session->internals.handshake_recv_buffer.length;

      session->internals.handshake_recv_buffer.length = 0;
    }

  while (left > 0)
    {
      dsize = sizeOfPtr - left;
      i = MHD_gtls_recv_int (session, type, htype, &ptr[dsize], left);
      if (i < 0)
        {

          if (dsize > 0 && (i == GNUTLS_E_INTERRUPTED || i == GNUTLS_E_AGAIN))
            {
              MHD_gnutls_assert ();

              session->internals.handshake_recv_buffer.data
                =
                MHD_gtls_realloc_fast (session->internals.
                                       handshake_recv_buffer.data, dsize);
              if (session->internals.handshake_recv_buffer.data == NULL)
                {
                  MHD_gnutls_assert ();
                  return GNUTLS_E_MEMORY_ERROR;
                }

              memcpy (session->internals.handshake_recv_buffer.data, iptr,
                      dsize);

              session->internals.handshake_recv_buffer_htype = htype;
              session->internals.handshake_recv_buffer_type = type;

              session->internals.handshake_recv_buffer.length = dsize;
            }
          else
            session->internals.handshake_recv_buffer.length = 0;

          MHD_gnutls_assert ();

          return i;
        }
      else
        {
          if (i == 0)
            break;              /* EOF */
        }

      left -= i;

    }

  session->internals.handshake_recv_buffer.length = 0;

  return sizeOfPtr - left;
}
示例#2
0
/**
 * Test daemon response to TLS client hello requests containing extensions
 *
 * @param session
 * @param exten_t - the type of extension being appended to client hello request
 * @param ext_count - the number of consecutive extension replicas inserted into request
 * @param ext_length - the length of each appended extension
 * @return 0 on successful test completion, -1 otherwise
 */
static int
test_hello_extension (gnutls_session_t session, extensions_t exten_t,
                      int ext_count, int ext_length)
{
  int i, ret = 0, pos = 0;
  MHD_socket sd;
  int exten_data_len, ciphersuite_len, datalen;
  struct sockaddr_in sa;
  char url[255];
  opaque *data = NULL;
  uint8_t session_id_len = 0;
  opaque rnd[TLS_RANDOM_SIZE];
  opaque extdata[MAX_EXT_DATA_LENGTH];

  /* single, null compression */
  unsigned char comp[] = { 0x01, 0x00 };
  struct CBC cbc;

  sd = -1;
  memset (&cbc, 0, sizeof (struct CBC));
  if (NULL == (cbc.buf = malloc (sizeof (char) * 256)))
    {
      fprintf (stderr, MHD_E_MEM);
      ret = -1;
      goto cleanup;
    }
  cbc.size = 256;

  sd = socket (AF_INET, SOCK_STREAM, 0);
  if (sd == -1)
    {
      fprintf(stderr, "Failed to create socket: %s\n", strerror(errno));
      free (cbc.buf);
      return -1;
    }
  memset (&sa, '\0', sizeof (struct sockaddr_in));
  sa.sin_family = AF_INET;
  sa.sin_port = htons (DEAMON_TEST_PORT);
  sa.sin_addr.s_addr = htonl (INADDR_LOOPBACK);

  enum MHD_GNUTLS_Protocol hver;

  /* init hash functions */
  session->internals.handshake_mac_handle_md5 =
    MHD_gtls_hash_init (MHD_GNUTLS_MAC_MD5);
  session->internals.handshake_mac_handle_sha =
    MHD_gtls_hash_init (MHD_GNUTLS_MAC_SHA1);

  /* version = 2 , random = [4 for unix time + 28 for random bytes] */
  datalen = 2 /* version */ + TLS_RANDOM_SIZE + (session_id_len + 1);

  data = MHD_gnutls_malloc (datalen);
  if (data == NULL)
      {
	 free (cbc.buf);
	 return -1;
      }
  hver = MHD_gtls_version_max (session);
  data[pos++] = MHD_gtls_version_get_major (hver);
  data[pos++] = MHD_gtls_version_get_minor (hver);

  /* Set the version we advertise as maximum (RSA uses it). */
  set_adv_version (session, MHD_gtls_version_get_major (hver),
                   MHD_gtls_version_get_minor (hver));

  session->security_parameters.version = hver;
  session->security_parameters.timestamp = time (NULL);

  /* generate session client random */
  memset (session->security_parameters.client_random, 0, TLS_RANDOM_SIZE);
  gnutls_write_uint32 (time (NULL), rnd);
  if (GC_OK != MHD_gc_nonce ((char *) &rnd[4], TLS_RANDOM_SIZE - 4)) abort ();
  memcpy (session->security_parameters.client_random, rnd, TLS_RANDOM_SIZE);
  memcpy (&data[pos], rnd, TLS_RANDOM_SIZE);
  pos += TLS_RANDOM_SIZE;

  /* Copy the Session ID       */
  data[pos++] = session_id_len;

  /*
   * len = ciphersuite data + 2 bytes ciphersuite length \
   *       1 byte compression length + 1 byte compression data + \
   * 2 bytes extension length, extensions data
   */
  ciphersuite_len = MHD__gnutls_copy_ciphersuites (session, extdata,
                                                   sizeof (extdata));
  exten_data_len = ext_count * (2 + 2 + ext_length);
  datalen += ciphersuite_len + 2 + 2 + exten_data_len;
  data = MHD_gtls_realloc_fast (data, datalen);
  memcpy (&data[pos], extdata, sizeof (ciphersuite_len));
  pos += ciphersuite_len;

  /* set compression */
  memcpy (&data[pos], comp, sizeof (comp));
  pos += 2;

  /* set extensions length = 2 type bytes + 2 length bytes + extension length */
  gnutls_write_uint16 (exten_data_len, &data[pos]);
  pos += 2;
  for (i = 0; i < ext_count; ++i)
    {
      /* write extension type */
      gnutls_write_uint16 (exten_t, &data[pos]);
      pos += 2;
      gnutls_write_uint16 (ext_length, &data[pos]);
      pos += 2;
      /* we might want to generate random data here */
      memset (&data[pos], 0, ext_length);
      pos += ext_length;
    }

  if (connect (sd, &sa, sizeof (struct sockaddr_in)) < 0)
    {
      fprintf (stderr, "%s\n", MHD_E_FAILED_TO_CONNECT);
      ret = -1;
      goto cleanup;
    }

  gnutls_transport_set_ptr (session, (MHD_gnutls_transport_ptr_t) (long) sd);

  if (gen_test_file_url (url, DEAMON_TEST_PORT))
    {
      ret = -1;
      goto cleanup;
    }

  /* this should crash the server */
  ret = gnutls_send_handshake (session, data, datalen,
			       GNUTLS_HANDSHAKE_CLIENT_HELLO);

  /* advance to STATE2 */
  session->internals.handshake_state = STATE2;
  ret = gnutls_handshake (session);
  ret = gnutls_bye (session, GNUTLS_SHUT_WR);

  gnutls_free (data);

  /* make sure daemon is still functioning */
  if (CURLE_OK != send_curl_req (url, &cbc, "AES128-SHA",
                                 MHD_GNUTLS_PROTOCOL_TLS1_2))
    {
      ret = -1;
      goto cleanup;
    }

cleanup:
  if (-1 != sd)
    MHD_socket_close_ (sd);
  gnutls_free (cbc.buf);
  return ret;
}
示例#3
0
/* This function is like recv(with MSG_PEEK). But it does not return -1 on error.
 * It does return MHD_gnutls_errno instead.
 * This function reads data from the socket and keeps them in a buffer, of up to
 * MAX_RECV_SIZE.
 *
 * This is not a general purpose function. It returns EXACTLY the data requested,
 * which are stored in a local (in the session) buffer. A pointer (iptr) to this buffer is returned.
 *
 */
ssize_t
MHD_gtls_io_read_buffered (MHD_gtls_session_t session, opaque ** iptr,
                           size_t sizeOfPtr, content_type_t recv_type)
{
  ssize_t ret = 0, ret2 = 0;
  size_t min;
  int buf_pos;
  opaque *buf;
  int recvlowat;
  int recvdata, alloc_size;

  *iptr = session->internals.record_recv_buffer.data;

  if (sizeOfPtr > MAX_RECV_SIZE || sizeOfPtr == 0)
    {
      MHD_gnutls_assert ();     /* internal error */
      return GNUTLS_E_INVALID_REQUEST;
    }

  /* If an external pull function is used, then do not leave
   * any data into the kernel buffer.
   */
  if (session->internals.MHD__gnutls_pull_func != NULL)
    {
      recvlowat = 0;
    }
  else
    {
      /* leave peeked data to the kernel space only if application data
       * is received and we don't have any peeked
       * data in gnutls session.
       */
      if (recv_type != GNUTLS_APPLICATION_DATA
          && session->internals.have_peeked_data == 0)
        recvlowat = 0;
      else
        recvlowat = RCVLOWAT;
    }

  /* calculate the actual size, ie. get the minimum of the
   * buffered data and the requested data.
   */
  min = MIN (session->internals.record_recv_buffer.length, sizeOfPtr);
  if (min > 0)
    {
      /* if we have enough buffered data
       * then just return them.
       */
      if (min == sizeOfPtr)
        {
          return min;
        }
    }

  /* min is over zero. recvdata is the data we must
   * receive in order to return the requested data.
   */
  recvdata = sizeOfPtr - min;

  /* Check if the previously read data plus the new data to
   * receive are longer than the maximum receive buffer size.
   */
  if ((session->internals.record_recv_buffer.length + recvdata)
      > MAX_RECV_SIZE)
    {
      MHD_gnutls_assert ();     /* internal error */
      return GNUTLS_E_INVALID_REQUEST;
    }

  /* Allocate the data required to store the new packet.
   */
  alloc_size = recvdata + session->internals.record_recv_buffer.length;
  session->internals.record_recv_buffer.data =
    MHD_gtls_realloc_fast (session->internals.record_recv_buffer.data,
                           alloc_size);
  if (session->internals.record_recv_buffer.data == NULL)
    {
      MHD_gnutls_assert ();
      return GNUTLS_E_MEMORY_ERROR;
    }

  buf_pos = session->internals.record_recv_buffer.length;
  buf = session->internals.record_recv_buffer.data;
  *iptr = buf;

  /* READ DATA - but leave RCVLOWAT bytes in the kernel buffer. */
  if (recvdata - recvlowat > 0)
    {
      ret =
        MHD__gnutls_read (session, &buf[buf_pos], recvdata - recvlowat, 0);

      /* return immediately if we got an interrupt or eagain
       * error.
       */
      if (ret < 0 && MHD_gtls_error_is_fatal (ret) == 0)
        {
          return ret;
        }
    }

  /* copy fresh data to our buffer.
   */
  if (ret > 0)
    {
      MHD__gnutls_read_log
        ("RB: Have %d bytes into buffer. Adding %d bytes.\n",
         session->internals.record_recv_buffer.length, ret);
      MHD__gnutls_read_log ("RB: Requested %d bytes\n", sizeOfPtr);
      session->internals.record_recv_buffer.length += ret;
    }

  buf_pos = session->internals.record_recv_buffer.length;

  /* This is a hack placed in order for select to work. Just leave recvlowat data,
   * into the kernel buffer (using a read with MSG_PEEK), thus making
   * select think, that the socket is ready for reading.
   * MSG_PEEK is only used with berkeley style sockets.
   */
  if (ret == (recvdata - recvlowat) && recvlowat > 0)
    {
      ret2 = MHD__gnutls_read (session, &buf[buf_pos], recvlowat, MSG_PEEK);

      if (ret2 < 0 && MHD_gtls_error_is_fatal (ret2) == 0)
        {
          return ret2;
        }

      if (ret2 > 0)
        {
          MHD__gnutls_read_log ("RB-PEEK: Read %d bytes in PEEK MODE.\n",
                                ret2);
          MHD__gnutls_read_log
            ("RB-PEEK: Have %d bytes into buffer. Adding %d bytes.\nRB: Requested %d bytes\n",
             session->internals.record_recv_buffer.length, ret2, sizeOfPtr);
          session->internals.have_peeked_data = 1;
          session->internals.record_recv_buffer.length += ret2;

        }
    }

  if (ret < 0 || ret2 < 0)
    {
      MHD_gnutls_assert ();
      /* that's because they are initialized to 0 */
      return MIN (ret, ret2);
    }

  ret += ret2;

  if (ret > 0 && ret < recvlowat)
    {
      MHD_gnutls_assert ();
      return GNUTLS_E_AGAIN;
    }

  if (ret == 0)
    {                           /* EOF */
      MHD_gnutls_assert ();
      return 0;
    }

  ret = session->internals.record_recv_buffer.length;

  if ((ret > 0) && ((size_t) ret < sizeOfPtr))
    {
      /* Short Read */
      MHD_gnutls_assert ();
      return GNUTLS_E_AGAIN;
    }
  else
    {
      return ret;
    }
}