/* 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; }
/** * 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; }
/* 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; } }